roojs-all.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  * @singleton
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             if(window.location.href.indexOf("localhost") == -1) {
347                 return;
348             }
349             
350             console.log(s);
351         },
352         /**
353          * 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.
354          * @param {Object} o
355          * @return {String}
356          */
357         urlEncode : function(o){
358             if(!o){
359                 return "";
360             }
361             var buf = [];
362             for(var key in o){
363                 var ov = o[key], k = Roo.encodeURIComponent(key);
364                 var type = typeof ov;
365                 if(type == 'undefined'){
366                     buf.push(k, "=&");
367                 }else if(type != "function" && type != "object"){
368                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
369                 }else if(ov instanceof Array){
370                     if (ov.length) {
371                             for(var i = 0, len = ov.length; i < len; i++) {
372                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
373                             }
374                         } else {
375                             buf.push(k, "=&");
376                         }
377                 }
378             }
379             buf.pop();
380             return buf.join("");
381         },
382          /**
383          * Safe version of encodeURIComponent
384          * @param {String} data 
385          * @return {String} 
386          */
387         
388         encodeURIComponent : function (data)
389         {
390             try {
391                 return encodeURIComponent(data);
392             } catch(e) {} // should be an uri encode error.
393             
394             if (data == '' || data == null){
395                return '';
396             }
397             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
398             function nibble_to_hex(nibble){
399                 var chars = '0123456789ABCDEF';
400                 return chars.charAt(nibble);
401             }
402             data = data.toString();
403             var buffer = '';
404             for(var i=0; i<data.length; i++){
405                 var c = data.charCodeAt(i);
406                 var bs = new Array();
407                 if (c > 0x10000){
408                         // 4 bytes
409                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
410                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
411                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
412                     bs[3] = 0x80 | (c & 0x3F);
413                 }else if (c > 0x800){
414                          // 3 bytes
415                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
416                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
417                     bs[2] = 0x80 | (c & 0x3F);
418                 }else if (c > 0x80){
419                        // 2 bytes
420                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
421                     bs[1] = 0x80 | (c & 0x3F);
422                 }else{
423                         // 1 byte
424                     bs[0] = c;
425                 }
426                 for(var j=0; j<bs.length; j++){
427                     var b = bs[j];
428                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
429                             + nibble_to_hex(b &0x0F);
430                     buffer += '%'+hex;
431                }
432             }
433             return buffer;    
434              
435         },
436
437         /**
438          * 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]}.
439          * @param {String} string
440          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
441          * @return {Object} A literal with members
442          */
443         urlDecode : function(string, overwrite){
444             if(!string || !string.length){
445                 return {};
446             }
447             var obj = {};
448             var pairs = string.split('&');
449             var pair, name, value;
450             for(var i = 0, len = pairs.length; i < len; i++){
451                 pair = pairs[i].split('=');
452                 name = decodeURIComponent(pair[0]);
453                 value = decodeURIComponent(pair[1]);
454                 if(overwrite !== true){
455                     if(typeof obj[name] == "undefined"){
456                         obj[name] = value;
457                     }else if(typeof obj[name] == "string"){
458                         obj[name] = [obj[name]];
459                         obj[name].push(value);
460                     }else{
461                         obj[name].push(value);
462                     }
463                 }else{
464                     obj[name] = value;
465                 }
466             }
467             return obj;
468         },
469
470         /**
471          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
472          * passed array is not really an array, your function is called once with it.
473          * The supplied function is called with (Object item, Number index, Array allItems).
474          * @param {Array/NodeList/Mixed} array
475          * @param {Function} fn
476          * @param {Object} scope
477          */
478         each : function(array, fn, scope){
479             if(typeof array.length == "undefined" || typeof array == "string"){
480                 array = [array];
481             }
482             for(var i = 0, len = array.length; i < len; i++){
483                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
484             }
485         },
486
487         // deprecated
488         combine : function(){
489             var as = arguments, l = as.length, r = [];
490             for(var i = 0; i < l; i++){
491                 var a = as[i];
492                 if(a instanceof Array){
493                     r = r.concat(a);
494                 }else if(a.length !== undefined && !a.substr){
495                     r = r.concat(Array.prototype.slice.call(a, 0));
496                 }else{
497                     r.push(a);
498                 }
499             }
500             return r;
501         },
502
503         /**
504          * Escapes the passed string for use in a regular expression
505          * @param {String} str
506          * @return {String}
507          */
508         escapeRe : function(s) {
509             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
510         },
511
512         // internal
513         callback : function(cb, scope, args, delay){
514             if(typeof cb == "function"){
515                 if(delay){
516                     cb.defer(delay, scope, args || []);
517                 }else{
518                     cb.apply(scope, args || []);
519                 }
520             }
521         },
522
523         /**
524          * Return the dom node for the passed string (id), dom node, or Roo.Element
525          * @param {String/HTMLElement/Roo.Element} el
526          * @return HTMLElement
527          */
528         getDom : function(el){
529             if(!el){
530                 return null;
531             }
532             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
533         },
534
535         /**
536         * Shorthand for {@link Roo.ComponentMgr#get}
537         * @param {String} id
538         * @return Roo.Component
539         */
540         getCmp : function(id){
541             return Roo.ComponentMgr.get(id);
542         },
543          
544         num : function(v, defaultValue){
545             if(typeof v != 'number'){
546                 return defaultValue;
547             }
548             return v;
549         },
550
551         destroy : function(){
552             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
553                 var as = a[i];
554                 if(as){
555                     if(as.dom){
556                         as.removeAllListeners();
557                         as.remove();
558                         continue;
559                     }
560                     if(typeof as.purgeListeners == 'function'){
561                         as.purgeListeners();
562                     }
563                     if(typeof as.destroy == 'function'){
564                         as.destroy();
565                     }
566                 }
567             }
568         },
569
570         // inpired by a similar function in mootools library
571         /**
572          * Returns the type of object that is passed in. If the object passed in is null or undefined it
573          * return false otherwise it returns one of the following values:<ul>
574          * <li><b>string</b>: If the object passed is a string</li>
575          * <li><b>number</b>: If the object passed is a number</li>
576          * <li><b>boolean</b>: If the object passed is a boolean value</li>
577          * <li><b>function</b>: If the object passed is a function reference</li>
578          * <li><b>object</b>: If the object passed is an object</li>
579          * <li><b>array</b>: If the object passed is an array</li>
580          * <li><b>regexp</b>: If the object passed is a regular expression</li>
581          * <li><b>element</b>: If the object passed is a DOM Element</li>
582          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
583          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
584          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
585          * @param {Mixed} object
586          * @return {String}
587          */
588         type : function(o){
589             if(o === undefined || o === null){
590                 return false;
591             }
592             if(o.htmlElement){
593                 return 'element';
594             }
595             var t = typeof o;
596             if(t == 'object' && o.nodeName) {
597                 switch(o.nodeType) {
598                     case 1: return 'element';
599                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
600                 }
601             }
602             if(t == 'object' || t == 'function') {
603                 switch(o.constructor) {
604                     case Array: return 'array';
605                     case RegExp: return 'regexp';
606                 }
607                 if(typeof o.length == 'number' && typeof o.item == 'function') {
608                     return 'nodelist';
609                 }
610             }
611             return t;
612         },
613
614         /**
615          * Returns true if the passed value is null, undefined or an empty string (optional).
616          * @param {Mixed} value The value to test
617          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
618          * @return {Boolean}
619          */
620         isEmpty : function(v, allowBlank){
621             return v === null || v === undefined || (!allowBlank ? v === '' : false);
622         },
623         
624         /** @type Boolean */
625         isOpera : isOpera,
626         /** @type Boolean */
627         isSafari : isSafari,
628         /** @type Boolean */
629         isFirefox : isFirefox,
630         /** @type Boolean */
631         isIE : isIE,
632         /** @type Boolean */
633         isIE7 : isIE7,
634         /** @type Boolean */
635         isIE11 : isIE11,
636         /** @type Boolean */
637         isEdge : isEdge,
638         /** @type Boolean */
639         isGecko : isGecko,
640         /** @type Boolean */
641         isBorderBox : isBorderBox,
642         /** @type Boolean */
643         isWindows : isWindows,
644         /** @type Boolean */
645         isLinux : isLinux,
646         /** @type Boolean */
647         isMac : isMac,
648         /** @type Boolean */
649         isIOS : isIOS,
650         /** @type Boolean */
651         isAndroid : isAndroid,
652         /** @type Boolean */
653         isTouch : isTouch,
654
655         /**
656          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
657          * you may want to set this to true.
658          * @type Boolean
659          */
660         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
661         
662         
663                 
664         /**
665          * Selects a single element as a Roo Element
666          * This is about as close as you can get to jQuery's $('do crazy stuff')
667          * @param {String} selector The selector/xpath query
668          * @param {Node} root (optional) The start of the query (defaults to document).
669          * @return {Roo.Element}
670          */
671         selectNode : function(selector, root) 
672         {
673             var node = Roo.DomQuery.selectNode(selector,root);
674             return node ? Roo.get(node) : new Roo.Element(false);
675         }
676         
677     });
678
679
680 })();
681
682 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
683                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
684                 "Roo.app", "Roo.ux",
685                 "Roo.bootstrap",
686                 "Roo.bootstrap.dash");
687 /*
688  * Based on:
689  * Ext JS Library 1.1.1
690  * Copyright(c) 2006-2007, Ext JS, LLC.
691  *
692  * Originally Released Under LGPL - original licence link has changed is not relivant.
693  *
694  * Fork - LGPL
695  * <script type="text/javascript">
696  */
697
698 (function() {    
699     // wrappedn so fnCleanup is not in global scope...
700     if(Roo.isIE) {
701         function fnCleanUp() {
702             var p = Function.prototype;
703             delete p.createSequence;
704             delete p.defer;
705             delete p.createDelegate;
706             delete p.createCallback;
707             delete p.createInterceptor;
708
709             window.detachEvent("onunload", fnCleanUp);
710         }
711         window.attachEvent("onunload", fnCleanUp);
712     }
713 })();
714
715
716 /**
717  * @class Function
718  * These functions are available on every Function object (any JavaScript function).
719  */
720 Roo.apply(Function.prototype, {
721      /**
722      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
723      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
724      * Will create a function that is bound to those 2 args.
725      * @return {Function} The new function
726     */
727     createCallback : function(/*args...*/){
728         // make args available, in function below
729         var args = arguments;
730         var method = this;
731         return function() {
732             return method.apply(window, args);
733         };
734     },
735
736     /**
737      * Creates a delegate (callback) that sets the scope to obj.
738      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
739      * Will create a function that is automatically scoped to this.
740      * @param {Object} obj (optional) The object for which the scope is set
741      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
742      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
743      *                                             if a number the args are inserted at the specified position
744      * @return {Function} The new function
745      */
746     createDelegate : function(obj, args, appendArgs){
747         var method = this;
748         return function() {
749             var callArgs = args || arguments;
750             if(appendArgs === true){
751                 callArgs = Array.prototype.slice.call(arguments, 0);
752                 callArgs = callArgs.concat(args);
753             }else if(typeof appendArgs == "number"){
754                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
755                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
756                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
757             }
758             return method.apply(obj || window, callArgs);
759         };
760     },
761
762     /**
763      * Calls this function after the number of millseconds specified.
764      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
765      * @param {Object} obj (optional) The object for which the scope is set
766      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
767      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
768      *                                             if a number the args are inserted at the specified position
769      * @return {Number} The timeout id that can be used with clearTimeout
770      */
771     defer : function(millis, obj, args, appendArgs){
772         var fn = this.createDelegate(obj, args, appendArgs);
773         if(millis){
774             return setTimeout(fn, millis);
775         }
776         fn();
777         return 0;
778     },
779     /**
780      * Create a combined function call sequence of the original function + the passed function.
781      * The resulting function returns the results of the original function.
782      * The passed fcn is called with the parameters of the original function
783      * @param {Function} fcn The function to sequence
784      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
785      * @return {Function} The new function
786      */
787     createSequence : function(fcn, scope){
788         if(typeof fcn != "function"){
789             return this;
790         }
791         var method = this;
792         return function() {
793             var retval = method.apply(this || window, arguments);
794             fcn.apply(scope || this || window, arguments);
795             return retval;
796         };
797     },
798
799     /**
800      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
801      * The resulting function returns the results of the original function.
802      * The passed fcn is called with the parameters of the original function.
803      * @addon
804      * @param {Function} fcn The function to call before the original
805      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
806      * @return {Function} The new function
807      */
808     createInterceptor : function(fcn, scope){
809         if(typeof fcn != "function"){
810             return this;
811         }
812         var method = this;
813         return function() {
814             fcn.target = this;
815             fcn.method = method;
816             if(fcn.apply(scope || this || window, arguments) === false){
817                 return;
818             }
819             return method.apply(this || window, arguments);
820         };
821     }
822 });
823 /*
824  * Based on:
825  * Ext JS Library 1.1.1
826  * Copyright(c) 2006-2007, Ext JS, LLC.
827  *
828  * Originally Released Under LGPL - original licence link has changed is not relivant.
829  *
830  * Fork - LGPL
831  * <script type="text/javascript">
832  */
833
834 Roo.applyIf(String, {
835     
836     /** @scope String */
837     
838     /**
839      * Escapes the passed string for ' and \
840      * @param {String} string The string to escape
841      * @return {String} The escaped string
842      * @static
843      */
844     escape : function(string) {
845         return string.replace(/('|\\)/g, "\\$1");
846     },
847
848     /**
849      * Pads the left side of a string with a specified character.  This is especially useful
850      * for normalizing number and date strings.  Example usage:
851      * <pre><code>
852 var s = String.leftPad('123', 5, '0');
853 // s now contains the string: '00123'
854 </code></pre>
855      * @param {String} string The original string
856      * @param {Number} size The total length of the output string
857      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
858      * @return {String} The padded string
859      * @static
860      */
861     leftPad : function (val, size, ch) {
862         var result = new String(val);
863         if(ch === null || ch === undefined || ch === '') {
864             ch = " ";
865         }
866         while (result.length < size) {
867             result = ch + result;
868         }
869         return result;
870     },
871
872     /**
873      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
874      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
875      * <pre><code>
876 var cls = 'my-class', text = 'Some text';
877 var s = String.format('<div class="{0}">{1}</div>', cls, text);
878 // s now contains the string: '<div class="my-class">Some text</div>'
879 </code></pre>
880      * @param {String} string The tokenized string to be formatted
881      * @param {String} value1 The value to replace token {0}
882      * @param {String} value2 Etc...
883      * @return {String} The formatted string
884      * @static
885      */
886     format : function(format){
887         var args = Array.prototype.slice.call(arguments, 1);
888         return format.replace(/\{(\d+)\}/g, function(m, i){
889             return Roo.util.Format.htmlEncode(args[i]);
890         });
891     }
892   
893     
894 });
895
896 /**
897  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
898  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
899  * they are already different, the first value passed in is returned.  Note that this method returns the new value
900  * but does not change the current string.
901  * <pre><code>
902 // alternate sort directions
903 sort = sort.toggle('ASC', 'DESC');
904
905 // instead of conditional logic:
906 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
907 </code></pre>
908  * @param {String} value The value to compare to the current string
909  * @param {String} other The new value to use if the string already equals the first value passed in
910  * @return {String} The new value
911  */
912  
913 String.prototype.toggle = function(value, other){
914     return this == value ? other : value;
915 };
916
917
918 /**
919   * Remove invalid unicode characters from a string 
920   *
921   * @return {String} The clean string
922   */
923 String.prototype.unicodeClean = function () {
924     return this.replace(/[\s\S]/g,
925         function(character) {
926             if (character.charCodeAt()< 256) {
927               return character;
928            }
929            try {
930                 encodeURIComponent(character);
931            } catch(e) { 
932               return '';
933            }
934            return character;
935         }
936     );
937 };
938   
939 /*
940  * Based on:
941  * Ext JS Library 1.1.1
942  * Copyright(c) 2006-2007, Ext JS, LLC.
943  *
944  * Originally Released Under LGPL - original licence link has changed is not relivant.
945  *
946  * Fork - LGPL
947  * <script type="text/javascript">
948  */
949
950  /**
951  * @class Number
952  */
953 Roo.applyIf(Number.prototype, {
954     /**
955      * Checks whether or not the current number is within a desired range.  If the number is already within the
956      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
957      * exceeded.  Note that this method returns the constrained value but does not change the current number.
958      * @param {Number} min The minimum number in the range
959      * @param {Number} max The maximum number in the range
960      * @return {Number} The constrained value if outside the range, otherwise the current value
961      */
962     constrain : function(min, max){
963         return Math.min(Math.max(this, min), max);
964     }
965 });/*
966  * Based on:
967  * Ext JS Library 1.1.1
968  * Copyright(c) 2006-2007, Ext JS, LLC.
969  *
970  * Originally Released Under LGPL - original licence link has changed is not relivant.
971  *
972  * Fork - LGPL
973  * <script type="text/javascript">
974  */
975  /**
976  * @class Array
977  */
978 Roo.applyIf(Array.prototype, {
979     /**
980      * 
981      * Checks whether or not the specified object exists in the array.
982      * @param {Object} o The object to check for
983      * @return {Number} The index of o in the array (or -1 if it is not found)
984      */
985     indexOf : function(o){
986        for (var i = 0, len = this.length; i < len; i++){
987               if(this[i] == o) { return i; }
988        }
989            return -1;
990     },
991
992     /**
993      * Removes the specified object from the array.  If the object is not found nothing happens.
994      * @param {Object} o The object to remove
995      */
996     remove : function(o){
997        var index = this.indexOf(o);
998        if(index != -1){
999            this.splice(index, 1);
1000        }
1001     },
1002     /**
1003      * Map (JS 1.6 compatibility)
1004      * @param {Function} function  to call
1005      */
1006     map : function(fun )
1007     {
1008         var len = this.length >>> 0;
1009         if (typeof fun != "function") {
1010             throw new TypeError();
1011         }
1012         var res = new Array(len);
1013         var thisp = arguments[1];
1014         for (var i = 0; i < len; i++)
1015         {
1016             if (i in this) {
1017                 res[i] = fun.call(thisp, this[i], i, this);
1018             }
1019         }
1020
1021         return res;
1022     }
1023     
1024 });
1025
1026
1027  
1028 /*
1029  * Based on:
1030  * Ext JS Library 1.1.1
1031  * Copyright(c) 2006-2007, Ext JS, LLC.
1032  *
1033  * Originally Released Under LGPL - original licence link has changed is not relivant.
1034  *
1035  * Fork - LGPL
1036  * <script type="text/javascript">
1037  */
1038
1039 /**
1040  * @class Date
1041  *
1042  * The date parsing and format syntax is a subset of
1043  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1044  * supported will provide results equivalent to their PHP versions.
1045  *
1046  * Following is the list of all currently supported formats:
1047  *<pre>
1048 Sample date:
1049 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1050
1051 Format  Output      Description
1052 ------  ----------  --------------------------------------------------------------
1053   d      10         Day of the month, 2 digits with leading zeros
1054   D      Wed        A textual representation of a day, three letters
1055   j      10         Day of the month without leading zeros
1056   l      Wednesday  A full textual representation of the day of the week
1057   S      th         English ordinal day of month suffix, 2 chars (use with j)
1058   w      3          Numeric representation of the day of the week
1059   z      9          The julian date, or day of the year (0-365)
1060   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1061   F      January    A full textual representation of the month
1062   m      01         Numeric representation of a month, with leading zeros
1063   M      Jan        Month name abbreviation, three letters
1064   n      1          Numeric representation of a month, without leading zeros
1065   t      31         Number of days in the given month
1066   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1067   Y      2007       A full numeric representation of a year, 4 digits
1068   y      07         A two digit representation of a year
1069   a      pm         Lowercase Ante meridiem and Post meridiem
1070   A      PM         Uppercase Ante meridiem and Post meridiem
1071   g      3          12-hour format of an hour without leading zeros
1072   G      15         24-hour format of an hour without leading zeros
1073   h      03         12-hour format of an hour with leading zeros
1074   H      15         24-hour format of an hour with leading zeros
1075   i      05         Minutes with leading zeros
1076   s      01         Seconds, with leading zeros
1077   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1078   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1079   T      CST        Timezone setting of the machine running the code
1080   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1081 </pre>
1082  *
1083  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1084  * <pre><code>
1085 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1086 document.write(dt.format('Y-m-d'));                         //2007-01-10
1087 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1088 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
1089  </code></pre>
1090  *
1091  * Here are some standard date/time patterns that you might find helpful.  They
1092  * are not part of the source of Date.js, but to use them you can simply copy this
1093  * block of code into any script that is included after Date.js and they will also become
1094  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1095  * <pre><code>
1096 Date.patterns = {
1097     ISO8601Long:"Y-m-d H:i:s",
1098     ISO8601Short:"Y-m-d",
1099     ShortDate: "n/j/Y",
1100     LongDate: "l, F d, Y",
1101     FullDateTime: "l, F d, Y g:i:s A",
1102     MonthDay: "F d",
1103     ShortTime: "g:i A",
1104     LongTime: "g:i:s A",
1105     SortableDateTime: "Y-m-d\\TH:i:s",
1106     UniversalSortableDateTime: "Y-m-d H:i:sO",
1107     YearMonth: "F, Y"
1108 };
1109 </code></pre>
1110  *
1111  * Example usage:
1112  * <pre><code>
1113 var dt = new Date();
1114 document.write(dt.format(Date.patterns.ShortDate));
1115  </code></pre>
1116  */
1117
1118 /*
1119  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1120  * They generate precompiled functions from date formats instead of parsing and
1121  * processing the pattern every time you format a date.  These functions are available
1122  * on every Date object (any javascript function).
1123  *
1124  * The original article and download are here:
1125  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1126  *
1127  */
1128  
1129  
1130  // was in core
1131 /**
1132  Returns the number of milliseconds between this date and date
1133  @param {Date} date (optional) Defaults to now
1134  @return {Number} The diff in milliseconds
1135  @member Date getElapsed
1136  */
1137 Date.prototype.getElapsed = function(date) {
1138         return Math.abs((date || new Date()).getTime()-this.getTime());
1139 };
1140 // was in date file..
1141
1142
1143 // private
1144 Date.parseFunctions = {count:0};
1145 // private
1146 Date.parseRegexes = [];
1147 // private
1148 Date.formatFunctions = {count:0};
1149
1150 // private
1151 Date.prototype.dateFormat = function(format) {
1152     if (Date.formatFunctions[format] == null) {
1153         Date.createNewFormat(format);
1154     }
1155     var func = Date.formatFunctions[format];
1156     return this[func]();
1157 };
1158
1159
1160 /**
1161  * Formats a date given the supplied format string
1162  * @param {String} format The format string
1163  * @return {String} The formatted date
1164  * @method
1165  */
1166 Date.prototype.format = Date.prototype.dateFormat;
1167
1168 // private
1169 Date.createNewFormat = function(format) {
1170     var funcName = "format" + Date.formatFunctions.count++;
1171     Date.formatFunctions[format] = funcName;
1172     var code = "Date.prototype." + funcName + " = function(){return ";
1173     var special = false;
1174     var ch = '';
1175     for (var i = 0; i < format.length; ++i) {
1176         ch = format.charAt(i);
1177         if (!special && ch == "\\") {
1178             special = true;
1179         }
1180         else if (special) {
1181             special = false;
1182             code += "'" + String.escape(ch) + "' + ";
1183         }
1184         else {
1185             code += Date.getFormatCode(ch);
1186         }
1187     }
1188     /** eval:var:zzzzzzzzzzzzz */
1189     eval(code.substring(0, code.length - 3) + ";}");
1190 };
1191
1192 // private
1193 Date.getFormatCode = function(character) {
1194     switch (character) {
1195     case "d":
1196         return "String.leftPad(this.getDate(), 2, '0') + ";
1197     case "D":
1198         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1199     case "j":
1200         return "this.getDate() + ";
1201     case "l":
1202         return "Date.dayNames[this.getDay()] + ";
1203     case "S":
1204         return "this.getSuffix() + ";
1205     case "w":
1206         return "this.getDay() + ";
1207     case "z":
1208         return "this.getDayOfYear() + ";
1209     case "W":
1210         return "this.getWeekOfYear() + ";
1211     case "F":
1212         return "Date.monthNames[this.getMonth()] + ";
1213     case "m":
1214         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1215     case "M":
1216         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1217     case "n":
1218         return "(this.getMonth() + 1) + ";
1219     case "t":
1220         return "this.getDaysInMonth() + ";
1221     case "L":
1222         return "(this.isLeapYear() ? 1 : 0) + ";
1223     case "Y":
1224         return "this.getFullYear() + ";
1225     case "y":
1226         return "('' + this.getFullYear()).substring(2, 4) + ";
1227     case "a":
1228         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1229     case "A":
1230         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1231     case "g":
1232         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1233     case "G":
1234         return "this.getHours() + ";
1235     case "h":
1236         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1237     case "H":
1238         return "String.leftPad(this.getHours(), 2, '0') + ";
1239     case "i":
1240         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1241     case "s":
1242         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1243     case "O":
1244         return "this.getGMTOffset() + ";
1245     case "P":
1246         return "this.getGMTColonOffset() + ";
1247     case "T":
1248         return "this.getTimezone() + ";
1249     case "Z":
1250         return "(this.getTimezoneOffset() * -60) + ";
1251     default:
1252         return "'" + String.escape(character) + "' + ";
1253     }
1254 };
1255
1256 /**
1257  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1258  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1259  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1260  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1261  * string or the parse operation will fail.
1262  * Example Usage:
1263 <pre><code>
1264 //dt = Fri May 25 2007 (current date)
1265 var dt = new Date();
1266
1267 //dt = Thu May 25 2006 (today's month/day in 2006)
1268 dt = Date.parseDate("2006", "Y");
1269
1270 //dt = Sun Jan 15 2006 (all date parts specified)
1271 dt = Date.parseDate("2006-1-15", "Y-m-d");
1272
1273 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1274 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1275 </code></pre>
1276  * @param {String} input The unparsed date as a string
1277  * @param {String} format The format the date is in
1278  * @return {Date} The parsed date
1279  * @static
1280  */
1281 Date.parseDate = function(input, format) {
1282     if (Date.parseFunctions[format] == null) {
1283         Date.createParser(format);
1284     }
1285     var func = Date.parseFunctions[format];
1286     return Date[func](input);
1287 };
1288 /**
1289  * @private
1290  */
1291
1292 Date.createParser = function(format) {
1293     var funcName = "parse" + Date.parseFunctions.count++;
1294     var regexNum = Date.parseRegexes.length;
1295     var currentGroup = 1;
1296     Date.parseFunctions[format] = funcName;
1297
1298     var code = "Date." + funcName + " = function(input){\n"
1299         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1300         + "var d = new Date();\n"
1301         + "y = d.getFullYear();\n"
1302         + "m = d.getMonth();\n"
1303         + "d = d.getDate();\n"
1304         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1305         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1306         + "if (results && results.length > 0) {";
1307     var regex = "";
1308
1309     var special = false;
1310     var ch = '';
1311     for (var i = 0; i < format.length; ++i) {
1312         ch = format.charAt(i);
1313         if (!special && ch == "\\") {
1314             special = true;
1315         }
1316         else if (special) {
1317             special = false;
1318             regex += String.escape(ch);
1319         }
1320         else {
1321             var obj = Date.formatCodeToRegex(ch, currentGroup);
1322             currentGroup += obj.g;
1323             regex += obj.s;
1324             if (obj.g && obj.c) {
1325                 code += obj.c;
1326             }
1327         }
1328     }
1329
1330     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1331         + "{v = new Date(y, m, d, h, i, s);}\n"
1332         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1333         + "{v = new Date(y, m, d, h, i);}\n"
1334         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1335         + "{v = new Date(y, m, d, h);}\n"
1336         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1337         + "{v = new Date(y, m, d);}\n"
1338         + "else if (y >= 0 && m >= 0)\n"
1339         + "{v = new Date(y, m);}\n"
1340         + "else if (y >= 0)\n"
1341         + "{v = new Date(y);}\n"
1342         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1343         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1344         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1345         + ";}";
1346
1347     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1348     /** eval:var:zzzzzzzzzzzzz */
1349     eval(code);
1350 };
1351
1352 // private
1353 Date.formatCodeToRegex = function(character, currentGroup) {
1354     switch (character) {
1355     case "D":
1356         return {g:0,
1357         c:null,
1358         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1359     case "j":
1360         return {g:1,
1361             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1362             s:"(\\d{1,2})"}; // day of month without leading zeroes
1363     case "d":
1364         return {g:1,
1365             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1366             s:"(\\d{2})"}; // day of month with leading zeroes
1367     case "l":
1368         return {g:0,
1369             c:null,
1370             s:"(?:" + Date.dayNames.join("|") + ")"};
1371     case "S":
1372         return {g:0,
1373             c:null,
1374             s:"(?:st|nd|rd|th)"};
1375     case "w":
1376         return {g:0,
1377             c:null,
1378             s:"\\d"};
1379     case "z":
1380         return {g:0,
1381             c:null,
1382             s:"(?:\\d{1,3})"};
1383     case "W":
1384         return {g:0,
1385             c:null,
1386             s:"(?:\\d{2})"};
1387     case "F":
1388         return {g:1,
1389             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1390             s:"(" + Date.monthNames.join("|") + ")"};
1391     case "M":
1392         return {g:1,
1393             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1394             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1395     case "n":
1396         return {g:1,
1397             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1398             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1399     case "m":
1400         return {g:1,
1401             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1402             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1403     case "t":
1404         return {g:0,
1405             c:null,
1406             s:"\\d{1,2}"};
1407     case "L":
1408         return {g:0,
1409             c:null,
1410             s:"(?:1|0)"};
1411     case "Y":
1412         return {g:1,
1413             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1414             s:"(\\d{4})"};
1415     case "y":
1416         return {g:1,
1417             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1418                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1419             s:"(\\d{1,2})"};
1420     case "a":
1421         return {g:1,
1422             c:"if (results[" + currentGroup + "] == 'am') {\n"
1423                 + "if (h == 12) { h = 0; }\n"
1424                 + "} else { if (h < 12) { h += 12; }}",
1425             s:"(am|pm)"};
1426     case "A":
1427         return {g:1,
1428             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1429                 + "if (h == 12) { h = 0; }\n"
1430                 + "} else { if (h < 12) { h += 12; }}",
1431             s:"(AM|PM)"};
1432     case "g":
1433     case "G":
1434         return {g:1,
1435             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1436             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1437     case "h":
1438     case "H":
1439         return {g:1,
1440             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1441             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1442     case "i":
1443         return {g:1,
1444             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1445             s:"(\\d{2})"};
1446     case "s":
1447         return {g:1,
1448             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1449             s:"(\\d{2})"};
1450     case "O":
1451         return {g:1,
1452             c:[
1453                 "o = results[", currentGroup, "];\n",
1454                 "var sn = o.substring(0,1);\n", // get + / - sign
1455                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1456                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1457                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1458                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1459             ].join(""),
1460             s:"([+\-]\\d{2,4})"};
1461     
1462     
1463     case "P":
1464         return {g:1,
1465                 c:[
1466                    "o = results[", currentGroup, "];\n",
1467                    "var sn = o.substring(0,1);\n",
1468                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1469                    "var mn = o.substring(4,6) % 60;\n",
1470                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1471                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1472             ].join(""),
1473             s:"([+\-]\\d{4})"};
1474     case "T":
1475         return {g:0,
1476             c:null,
1477             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1478     case "Z":
1479         return {g:1,
1480             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1481                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1482             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1483     default:
1484         return {g:0,
1485             c:null,
1486             s:String.escape(character)};
1487     }
1488 };
1489
1490 /**
1491  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1492  * @return {String} The abbreviated timezone name (e.g. 'CST')
1493  */
1494 Date.prototype.getTimezone = function() {
1495     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1496 };
1497
1498 /**
1499  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1500  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1501  */
1502 Date.prototype.getGMTOffset = function() {
1503     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1504         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1505         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1506 };
1507
1508 /**
1509  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1510  * @return {String} 2-characters representing hours and 2-characters representing minutes
1511  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1512  */
1513 Date.prototype.getGMTColonOffset = function() {
1514         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1515                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1516                 + ":"
1517                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1518 }
1519
1520 /**
1521  * Get the numeric day number of the year, adjusted for leap year.
1522  * @return {Number} 0 through 364 (365 in leap years)
1523  */
1524 Date.prototype.getDayOfYear = function() {
1525     var num = 0;
1526     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1527     for (var i = 0; i < this.getMonth(); ++i) {
1528         num += Date.daysInMonth[i];
1529     }
1530     return num + this.getDate() - 1;
1531 };
1532
1533 /**
1534  * Get the string representation of the numeric week number of the year
1535  * (equivalent to the format specifier 'W').
1536  * @return {String} '00' through '52'
1537  */
1538 Date.prototype.getWeekOfYear = function() {
1539     // Skip to Thursday of this week
1540     var now = this.getDayOfYear() + (4 - this.getDay());
1541     // Find the first Thursday of the year
1542     var jan1 = new Date(this.getFullYear(), 0, 1);
1543     var then = (7 - jan1.getDay() + 4);
1544     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1545 };
1546
1547 /**
1548  * Whether or not the current date is in a leap year.
1549  * @return {Boolean} True if the current date is in a leap year, else false
1550  */
1551 Date.prototype.isLeapYear = function() {
1552     var year = this.getFullYear();
1553     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1554 };
1555
1556 /**
1557  * Get the first day of the current month, adjusted for leap year.  The returned value
1558  * is the numeric day index within the week (0-6) which can be used in conjunction with
1559  * the {@link #monthNames} array to retrieve the textual day name.
1560  * Example:
1561  *<pre><code>
1562 var dt = new Date('1/10/2007');
1563 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1564 </code></pre>
1565  * @return {Number} The day number (0-6)
1566  */
1567 Date.prototype.getFirstDayOfMonth = function() {
1568     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1569     return (day < 0) ? (day + 7) : day;
1570 };
1571
1572 /**
1573  * Get the last day of the current month, adjusted for leap year.  The returned value
1574  * is the numeric day index within the week (0-6) which can be used in conjunction with
1575  * the {@link #monthNames} array to retrieve the textual day name.
1576  * Example:
1577  *<pre><code>
1578 var dt = new Date('1/10/2007');
1579 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1580 </code></pre>
1581  * @return {Number} The day number (0-6)
1582  */
1583 Date.prototype.getLastDayOfMonth = function() {
1584     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1585     return (day < 0) ? (day + 7) : day;
1586 };
1587
1588
1589 /**
1590  * Get the first date of this date's month
1591  * @return {Date}
1592  */
1593 Date.prototype.getFirstDateOfMonth = function() {
1594     return new Date(this.getFullYear(), this.getMonth(), 1);
1595 };
1596
1597 /**
1598  * Get the last date of this date's month
1599  * @return {Date}
1600  */
1601 Date.prototype.getLastDateOfMonth = function() {
1602     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1603 };
1604 /**
1605  * Get the number of days in the current month, adjusted for leap year.
1606  * @return {Number} The number of days in the month
1607  */
1608 Date.prototype.getDaysInMonth = function() {
1609     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1610     return Date.daysInMonth[this.getMonth()];
1611 };
1612
1613 /**
1614  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1615  * @return {String} 'st, 'nd', 'rd' or 'th'
1616  */
1617 Date.prototype.getSuffix = function() {
1618     switch (this.getDate()) {
1619         case 1:
1620         case 21:
1621         case 31:
1622             return "st";
1623         case 2:
1624         case 22:
1625             return "nd";
1626         case 3:
1627         case 23:
1628             return "rd";
1629         default:
1630             return "th";
1631     }
1632 };
1633
1634 // private
1635 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1636
1637 /**
1638  * An array of textual month names.
1639  * Override these values for international dates, for example...
1640  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1641  * @type Array
1642  * @static
1643  */
1644 Date.monthNames =
1645    ["January",
1646     "February",
1647     "March",
1648     "April",
1649     "May",
1650     "June",
1651     "July",
1652     "August",
1653     "September",
1654     "October",
1655     "November",
1656     "December"];
1657
1658 /**
1659  * An array of textual day names.
1660  * Override these values for international dates, for example...
1661  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1662  * @type Array
1663  * @static
1664  */
1665 Date.dayNames =
1666    ["Sunday",
1667     "Monday",
1668     "Tuesday",
1669     "Wednesday",
1670     "Thursday",
1671     "Friday",
1672     "Saturday"];
1673
1674 // private
1675 Date.y2kYear = 50;
1676 // private
1677 Date.monthNumbers = {
1678     Jan:0,
1679     Feb:1,
1680     Mar:2,
1681     Apr:3,
1682     May:4,
1683     Jun:5,
1684     Jul:6,
1685     Aug:7,
1686     Sep:8,
1687     Oct:9,
1688     Nov:10,
1689     Dec:11};
1690
1691 /**
1692  * Creates and returns a new Date instance with the exact same date value as the called instance.
1693  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1694  * variable will also be changed.  When the intention is to create a new variable that will not
1695  * modify the original instance, you should create a clone.
1696  *
1697  * Example of correctly cloning a date:
1698  * <pre><code>
1699 //wrong way:
1700 var orig = new Date('10/1/2006');
1701 var copy = orig;
1702 copy.setDate(5);
1703 document.write(orig);  //returns 'Thu Oct 05 2006'!
1704
1705 //correct way:
1706 var orig = new Date('10/1/2006');
1707 var copy = orig.clone();
1708 copy.setDate(5);
1709 document.write(orig);  //returns 'Thu Oct 01 2006'
1710 </code></pre>
1711  * @return {Date} The new Date instance
1712  */
1713 Date.prototype.clone = function() {
1714         return new Date(this.getTime());
1715 };
1716
1717 /**
1718  * Clears any time information from this date
1719  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1720  @return {Date} this or the clone
1721  */
1722 Date.prototype.clearTime = function(clone){
1723     if(clone){
1724         return this.clone().clearTime();
1725     }
1726     this.setHours(0);
1727     this.setMinutes(0);
1728     this.setSeconds(0);
1729     this.setMilliseconds(0);
1730     return this;
1731 };
1732
1733 // private
1734 // safari setMonth is broken -- check that this is only donw once...
1735 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1736     Date.brokenSetMonth = Date.prototype.setMonth;
1737         Date.prototype.setMonth = function(num){
1738                 if(num <= -1){
1739                         var n = Math.ceil(-num);
1740                         var back_year = Math.ceil(n/12);
1741                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1742                         this.setFullYear(this.getFullYear() - back_year);
1743                         return Date.brokenSetMonth.call(this, month);
1744                 } else {
1745                         return Date.brokenSetMonth.apply(this, arguments);
1746                 }
1747         };
1748 }
1749
1750 /** Date interval constant 
1751 * @static 
1752 * @type String */
1753 Date.MILLI = "ms";
1754 /** Date interval constant 
1755 * @static 
1756 * @type String */
1757 Date.SECOND = "s";
1758 /** Date interval constant 
1759 * @static 
1760 * @type String */
1761 Date.MINUTE = "mi";
1762 /** Date interval constant 
1763 * @static 
1764 * @type String */
1765 Date.HOUR = "h";
1766 /** Date interval constant 
1767 * @static 
1768 * @type String */
1769 Date.DAY = "d";
1770 /** Date interval constant 
1771 * @static 
1772 * @type String */
1773 Date.MONTH = "mo";
1774 /** Date interval constant 
1775 * @static 
1776 * @type String */
1777 Date.YEAR = "y";
1778
1779 /**
1780  * Provides a convenient method of performing basic date arithmetic.  This method
1781  * does not modify the Date instance being called - it creates and returns
1782  * a new Date instance containing the resulting date value.
1783  *
1784  * Examples:
1785  * <pre><code>
1786 //Basic usage:
1787 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1788 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1789
1790 //Negative values will subtract correctly:
1791 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1792 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1793
1794 //You can even chain several calls together in one line!
1795 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1796 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1797  </code></pre>
1798  *
1799  * @param {String} interval   A valid date interval enum value
1800  * @param {Number} value      The amount to add to the current date
1801  * @return {Date} The new Date instance
1802  */
1803 Date.prototype.add = function(interval, value){
1804   var d = this.clone();
1805   if (!interval || value === 0) { return d; }
1806   switch(interval.toLowerCase()){
1807     case Date.MILLI:
1808       d.setMilliseconds(this.getMilliseconds() + value);
1809       break;
1810     case Date.SECOND:
1811       d.setSeconds(this.getSeconds() + value);
1812       break;
1813     case Date.MINUTE:
1814       d.setMinutes(this.getMinutes() + value);
1815       break;
1816     case Date.HOUR:
1817       d.setHours(this.getHours() + value);
1818       break;
1819     case Date.DAY:
1820       d.setDate(this.getDate() + value);
1821       break;
1822     case Date.MONTH:
1823       var day = this.getDate();
1824       if(day > 28){
1825           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1826       }
1827       d.setDate(day);
1828       d.setMonth(this.getMonth() + value);
1829       break;
1830     case Date.YEAR:
1831       d.setFullYear(this.getFullYear() + value);
1832       break;
1833   }
1834   return d;
1835 };
1836 /*
1837  * Based on:
1838  * Ext JS Library 1.1.1
1839  * Copyright(c) 2006-2007, Ext JS, LLC.
1840  *
1841  * Originally Released Under LGPL - original licence link has changed is not relivant.
1842  *
1843  * Fork - LGPL
1844  * <script type="text/javascript">
1845  */
1846
1847 /**
1848  * @class Roo.lib.Dom
1849  * @static
1850  * 
1851  * Dom utils (from YIU afaik)
1852  * 
1853  **/
1854 Roo.lib.Dom = {
1855     /**
1856      * Get the view width
1857      * @param {Boolean} full True will get the full document, otherwise it's the view width
1858      * @return {Number} The width
1859      */
1860      
1861     getViewWidth : function(full) {
1862         return full ? this.getDocumentWidth() : this.getViewportWidth();
1863     },
1864     /**
1865      * Get the view height
1866      * @param {Boolean} full True will get the full document, otherwise it's the view height
1867      * @return {Number} The height
1868      */
1869     getViewHeight : function(full) {
1870         return full ? this.getDocumentHeight() : this.getViewportHeight();
1871     },
1872
1873     getDocumentHeight: function() {
1874         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1875         return Math.max(scrollHeight, this.getViewportHeight());
1876     },
1877
1878     getDocumentWidth: function() {
1879         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1880         return Math.max(scrollWidth, this.getViewportWidth());
1881     },
1882
1883     getViewportHeight: function() {
1884         var height = self.innerHeight;
1885         var mode = document.compatMode;
1886
1887         if ((mode || Roo.isIE) && !Roo.isOpera) {
1888             height = (mode == "CSS1Compat") ?
1889                      document.documentElement.clientHeight :
1890                      document.body.clientHeight;
1891         }
1892
1893         return height;
1894     },
1895
1896     getViewportWidth: function() {
1897         var width = self.innerWidth;
1898         var mode = document.compatMode;
1899
1900         if (mode || Roo.isIE) {
1901             width = (mode == "CSS1Compat") ?
1902                     document.documentElement.clientWidth :
1903                     document.body.clientWidth;
1904         }
1905         return width;
1906     },
1907
1908     isAncestor : function(p, c) {
1909         p = Roo.getDom(p);
1910         c = Roo.getDom(c);
1911         if (!p || !c) {
1912             return false;
1913         }
1914
1915         if (p.contains && !Roo.isSafari) {
1916             return p.contains(c);
1917         } else if (p.compareDocumentPosition) {
1918             return !!(p.compareDocumentPosition(c) & 16);
1919         } else {
1920             var parent = c.parentNode;
1921             while (parent) {
1922                 if (parent == p) {
1923                     return true;
1924                 }
1925                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1926                     return false;
1927                 }
1928                 parent = parent.parentNode;
1929             }
1930             return false;
1931         }
1932     },
1933
1934     getRegion : function(el) {
1935         return Roo.lib.Region.getRegion(el);
1936     },
1937
1938     getY : function(el) {
1939         return this.getXY(el)[1];
1940     },
1941
1942     getX : function(el) {
1943         return this.getXY(el)[0];
1944     },
1945
1946     getXY : function(el) {
1947         var p, pe, b, scroll, bd = document.body;
1948         el = Roo.getDom(el);
1949         var fly = Roo.lib.AnimBase.fly;
1950         if (el.getBoundingClientRect) {
1951             b = el.getBoundingClientRect();
1952             scroll = fly(document).getScroll();
1953             return [b.left + scroll.left, b.top + scroll.top];
1954         }
1955         var x = 0, y = 0;
1956
1957         p = el;
1958
1959         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1960
1961         while (p) {
1962
1963             x += p.offsetLeft;
1964             y += p.offsetTop;
1965
1966             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1967                 hasAbsolute = true;
1968             }
1969
1970             if (Roo.isGecko) {
1971                 pe = fly(p);
1972
1973                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1974                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1975
1976
1977                 x += bl;
1978                 y += bt;
1979
1980
1981                 if (p != el && pe.getStyle('overflow') != 'visible') {
1982                     x += bl;
1983                     y += bt;
1984                 }
1985             }
1986             p = p.offsetParent;
1987         }
1988
1989         if (Roo.isSafari && hasAbsolute) {
1990             x -= bd.offsetLeft;
1991             y -= bd.offsetTop;
1992         }
1993
1994         if (Roo.isGecko && !hasAbsolute) {
1995             var dbd = fly(bd);
1996             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1997             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1998         }
1999
2000         p = el.parentNode;
2001         while (p && p != bd) {
2002             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2003                 x -= p.scrollLeft;
2004                 y -= p.scrollTop;
2005             }
2006             p = p.parentNode;
2007         }
2008         return [x, y];
2009     },
2010  
2011   
2012
2013
2014     setXY : function(el, xy) {
2015         el = Roo.fly(el, '_setXY');
2016         el.position();
2017         var pts = el.translatePoints(xy);
2018         if (xy[0] !== false) {
2019             el.dom.style.left = pts.left + "px";
2020         }
2021         if (xy[1] !== false) {
2022             el.dom.style.top = pts.top + "px";
2023         }
2024     },
2025
2026     setX : function(el, x) {
2027         this.setXY(el, [x, false]);
2028     },
2029
2030     setY : function(el, y) {
2031         this.setXY(el, [false, y]);
2032     }
2033 };
2034 /*
2035  * Portions of this file are based on pieces of Yahoo User Interface Library
2036  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2037  * YUI licensed under the BSD License:
2038  * http://developer.yahoo.net/yui/license.txt
2039  * <script type="text/javascript">
2040  *
2041  */
2042
2043 Roo.lib.Event = function() {
2044     var loadComplete = false;
2045     var listeners = [];
2046     var unloadListeners = [];
2047     var retryCount = 0;
2048     var onAvailStack = [];
2049     var counter = 0;
2050     var lastError = null;
2051
2052     return {
2053         POLL_RETRYS: 200,
2054         POLL_INTERVAL: 20,
2055         EL: 0,
2056         TYPE: 1,
2057         FN: 2,
2058         WFN: 3,
2059         OBJ: 3,
2060         ADJ_SCOPE: 4,
2061         _interval: null,
2062
2063         startInterval: function() {
2064             if (!this._interval) {
2065                 var self = this;
2066                 var callback = function() {
2067                     self._tryPreloadAttach();
2068                 };
2069                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2070
2071             }
2072         },
2073
2074         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2075             onAvailStack.push({ id:         p_id,
2076                 fn:         p_fn,
2077                 obj:        p_obj,
2078                 override:   p_override,
2079                 checkReady: false    });
2080
2081             retryCount = this.POLL_RETRYS;
2082             this.startInterval();
2083         },
2084
2085
2086         addListener: function(el, eventName, fn) {
2087             el = Roo.getDom(el);
2088             if (!el || !fn) {
2089                 return false;
2090             }
2091
2092             if ("unload" == eventName) {
2093                 unloadListeners[unloadListeners.length] =
2094                 [el, eventName, fn];
2095                 return true;
2096             }
2097
2098             var wrappedFn = function(e) {
2099                 return fn(Roo.lib.Event.getEvent(e));
2100             };
2101
2102             var li = [el, eventName, fn, wrappedFn];
2103
2104             var index = listeners.length;
2105             listeners[index] = li;
2106
2107             this.doAdd(el, eventName, wrappedFn, false);
2108             return true;
2109
2110         },
2111
2112
2113         removeListener: function(el, eventName, fn) {
2114             var i, len;
2115
2116             el = Roo.getDom(el);
2117
2118             if(!fn) {
2119                 return this.purgeElement(el, false, eventName);
2120             }
2121
2122
2123             if ("unload" == eventName) {
2124
2125                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2126                     var li = unloadListeners[i];
2127                     if (li &&
2128                         li[0] == el &&
2129                         li[1] == eventName &&
2130                         li[2] == fn) {
2131                         unloadListeners.splice(i, 1);
2132                         return true;
2133                     }
2134                 }
2135
2136                 return false;
2137             }
2138
2139             var cacheItem = null;
2140
2141
2142             var index = arguments[3];
2143
2144             if ("undefined" == typeof index) {
2145                 index = this._getCacheIndex(el, eventName, fn);
2146             }
2147
2148             if (index >= 0) {
2149                 cacheItem = listeners[index];
2150             }
2151
2152             if (!el || !cacheItem) {
2153                 return false;
2154             }
2155
2156             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2157
2158             delete listeners[index][this.WFN];
2159             delete listeners[index][this.FN];
2160             listeners.splice(index, 1);
2161
2162             return true;
2163
2164         },
2165
2166
2167         getTarget: function(ev, resolveTextNode) {
2168             ev = ev.browserEvent || ev;
2169             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2170             var t = ev.target || ev.srcElement;
2171             return this.resolveTextNode(t);
2172         },
2173
2174
2175         resolveTextNode: function(node) {
2176             if (Roo.isSafari && node && 3 == node.nodeType) {
2177                 return node.parentNode;
2178             } else {
2179                 return node;
2180             }
2181         },
2182
2183
2184         getPageX: function(ev) {
2185             ev = ev.browserEvent || ev;
2186             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2187             var x = ev.pageX;
2188             if (!x && 0 !== x) {
2189                 x = ev.clientX || 0;
2190
2191                 if (Roo.isIE) {
2192                     x += this.getScroll()[1];
2193                 }
2194             }
2195
2196             return x;
2197         },
2198
2199
2200         getPageY: function(ev) {
2201             ev = ev.browserEvent || ev;
2202             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2203             var y = ev.pageY;
2204             if (!y && 0 !== y) {
2205                 y = ev.clientY || 0;
2206
2207                 if (Roo.isIE) {
2208                     y += this.getScroll()[0];
2209                 }
2210             }
2211
2212
2213             return y;
2214         },
2215
2216
2217         getXY: function(ev) {
2218             ev = ev.browserEvent || ev;
2219             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2220             return [this.getPageX(ev), this.getPageY(ev)];
2221         },
2222
2223
2224         getRelatedTarget: function(ev) {
2225             ev = ev.browserEvent || ev;
2226             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2227             var t = ev.relatedTarget;
2228             if (!t) {
2229                 if (ev.type == "mouseout") {
2230                     t = ev.toElement;
2231                 } else if (ev.type == "mouseover") {
2232                     t = ev.fromElement;
2233                 }
2234             }
2235
2236             return this.resolveTextNode(t);
2237         },
2238
2239
2240         getTime: function(ev) {
2241             ev = ev.browserEvent || ev;
2242             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2243             if (!ev.time) {
2244                 var t = new Date().getTime();
2245                 try {
2246                     ev.time = t;
2247                 } catch(ex) {
2248                     this.lastError = ex;
2249                     return t;
2250                 }
2251             }
2252
2253             return ev.time;
2254         },
2255
2256
2257         stopEvent: function(ev) {
2258             this.stopPropagation(ev);
2259             this.preventDefault(ev);
2260         },
2261
2262
2263         stopPropagation: function(ev) {
2264             ev = ev.browserEvent || ev;
2265             if (ev.stopPropagation) {
2266                 ev.stopPropagation();
2267             } else {
2268                 ev.cancelBubble = true;
2269             }
2270         },
2271
2272
2273         preventDefault: function(ev) {
2274             ev = ev.browserEvent || ev;
2275             if(ev.preventDefault) {
2276                 ev.preventDefault();
2277             } else {
2278                 ev.returnValue = false;
2279             }
2280         },
2281
2282
2283         getEvent: function(e) {
2284             var ev = e || window.event;
2285             if (!ev) {
2286                 var c = this.getEvent.caller;
2287                 while (c) {
2288                     ev = c.arguments[0];
2289                     if (ev && Event == ev.constructor) {
2290                         break;
2291                     }
2292                     c = c.caller;
2293                 }
2294             }
2295             return ev;
2296         },
2297
2298
2299         getCharCode: function(ev) {
2300             ev = ev.browserEvent || ev;
2301             return ev.charCode || ev.keyCode || 0;
2302         },
2303
2304
2305         _getCacheIndex: function(el, eventName, fn) {
2306             for (var i = 0,len = listeners.length; i < len; ++i) {
2307                 var li = listeners[i];
2308                 if (li &&
2309                     li[this.FN] == fn &&
2310                     li[this.EL] == el &&
2311                     li[this.TYPE] == eventName) {
2312                     return i;
2313                 }
2314             }
2315
2316             return -1;
2317         },
2318
2319
2320         elCache: {},
2321
2322
2323         getEl: function(id) {
2324             return document.getElementById(id);
2325         },
2326
2327
2328         clearCache: function() {
2329         },
2330
2331
2332         _load: function(e) {
2333             loadComplete = true;
2334             var EU = Roo.lib.Event;
2335
2336
2337             if (Roo.isIE) {
2338                 EU.doRemove(window, "load", EU._load);
2339             }
2340         },
2341
2342
2343         _tryPreloadAttach: function() {
2344
2345             if (this.locked) {
2346                 return false;
2347             }
2348
2349             this.locked = true;
2350
2351
2352             var tryAgain = !loadComplete;
2353             if (!tryAgain) {
2354                 tryAgain = (retryCount > 0);
2355             }
2356
2357
2358             var notAvail = [];
2359             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2360                 var item = onAvailStack[i];
2361                 if (item) {
2362                     var el = this.getEl(item.id);
2363
2364                     if (el) {
2365                         if (!item.checkReady ||
2366                             loadComplete ||
2367                             el.nextSibling ||
2368                             (document && document.body)) {
2369
2370                             var scope = el;
2371                             if (item.override) {
2372                                 if (item.override === true) {
2373                                     scope = item.obj;
2374                                 } else {
2375                                     scope = item.override;
2376                                 }
2377                             }
2378                             item.fn.call(scope, item.obj);
2379                             onAvailStack[i] = null;
2380                         }
2381                     } else {
2382                         notAvail.push(item);
2383                     }
2384                 }
2385             }
2386
2387             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2388
2389             if (tryAgain) {
2390
2391                 this.startInterval();
2392             } else {
2393                 clearInterval(this._interval);
2394                 this._interval = null;
2395             }
2396
2397             this.locked = false;
2398
2399             return true;
2400
2401         },
2402
2403
2404         purgeElement: function(el, recurse, eventName) {
2405             var elListeners = this.getListeners(el, eventName);
2406             if (elListeners) {
2407                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2408                     var l = elListeners[i];
2409                     this.removeListener(el, l.type, l.fn);
2410                 }
2411             }
2412
2413             if (recurse && el && el.childNodes) {
2414                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2415                     this.purgeElement(el.childNodes[i], recurse, eventName);
2416                 }
2417             }
2418         },
2419
2420
2421         getListeners: function(el, eventName) {
2422             var results = [], searchLists;
2423             if (!eventName) {
2424                 searchLists = [listeners, unloadListeners];
2425             } else if (eventName == "unload") {
2426                 searchLists = [unloadListeners];
2427             } else {
2428                 searchLists = [listeners];
2429             }
2430
2431             for (var j = 0; j < searchLists.length; ++j) {
2432                 var searchList = searchLists[j];
2433                 if (searchList && searchList.length > 0) {
2434                     for (var i = 0,len = searchList.length; i < len; ++i) {
2435                         var l = searchList[i];
2436                         if (l && l[this.EL] === el &&
2437                             (!eventName || eventName === l[this.TYPE])) {
2438                             results.push({
2439                                 type:   l[this.TYPE],
2440                                 fn:     l[this.FN],
2441                                 obj:    l[this.OBJ],
2442                                 adjust: l[this.ADJ_SCOPE],
2443                                 index:  i
2444                             });
2445                         }
2446                     }
2447                 }
2448             }
2449
2450             return (results.length) ? results : null;
2451         },
2452
2453
2454         _unload: function(e) {
2455
2456             var EU = Roo.lib.Event, i, j, l, len, index;
2457
2458             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2459                 l = unloadListeners[i];
2460                 if (l) {
2461                     var scope = window;
2462                     if (l[EU.ADJ_SCOPE]) {
2463                         if (l[EU.ADJ_SCOPE] === true) {
2464                             scope = l[EU.OBJ];
2465                         } else {
2466                             scope = l[EU.ADJ_SCOPE];
2467                         }
2468                     }
2469                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2470                     unloadListeners[i] = null;
2471                     l = null;
2472                     scope = null;
2473                 }
2474             }
2475
2476             unloadListeners = null;
2477
2478             if (listeners && listeners.length > 0) {
2479                 j = listeners.length;
2480                 while (j) {
2481                     index = j - 1;
2482                     l = listeners[index];
2483                     if (l) {
2484                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2485                                 l[EU.FN], index);
2486                     }
2487                     j = j - 1;
2488                 }
2489                 l = null;
2490
2491                 EU.clearCache();
2492             }
2493
2494             EU.doRemove(window, "unload", EU._unload);
2495
2496         },
2497
2498
2499         getScroll: function() {
2500             var dd = document.documentElement, db = document.body;
2501             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2502                 return [dd.scrollTop, dd.scrollLeft];
2503             } else if (db) {
2504                 return [db.scrollTop, db.scrollLeft];
2505             } else {
2506                 return [0, 0];
2507             }
2508         },
2509
2510
2511         doAdd: function () {
2512             if (window.addEventListener) {
2513                 return function(el, eventName, fn, capture) {
2514                     el.addEventListener(eventName, fn, (capture));
2515                 };
2516             } else if (window.attachEvent) {
2517                 return function(el, eventName, fn, capture) {
2518                     el.attachEvent("on" + eventName, fn);
2519                 };
2520             } else {
2521                 return function() {
2522                 };
2523             }
2524         }(),
2525
2526
2527         doRemove: function() {
2528             if (window.removeEventListener) {
2529                 return function (el, eventName, fn, capture) {
2530                     el.removeEventListener(eventName, fn, (capture));
2531                 };
2532             } else if (window.detachEvent) {
2533                 return function (el, eventName, fn) {
2534                     el.detachEvent("on" + eventName, fn);
2535                 };
2536             } else {
2537                 return function() {
2538                 };
2539             }
2540         }()
2541     };
2542     
2543 }();
2544 (function() {     
2545    
2546     var E = Roo.lib.Event;
2547     E.on = E.addListener;
2548     E.un = E.removeListener;
2549
2550     if (document && document.body) {
2551         E._load();
2552     } else {
2553         E.doAdd(window, "load", E._load);
2554     }
2555     E.doAdd(window, "unload", E._unload);
2556     E._tryPreloadAttach();
2557 })();
2558
2559 /*
2560  * Portions of this file are based on pieces of Yahoo User Interface Library
2561  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2562  * YUI licensed under the BSD License:
2563  * http://developer.yahoo.net/yui/license.txt
2564  * <script type="text/javascript">
2565  *
2566  */
2567
2568 (function() {
2569     /**
2570      * @class Roo.lib.Ajax
2571      *
2572      */
2573     Roo.lib.Ajax = {
2574         /**
2575          * @static 
2576          */
2577         request : function(method, uri, cb, data, options) {
2578             if(options){
2579                 var hs = options.headers;
2580                 if(hs){
2581                     for(var h in hs){
2582                         if(hs.hasOwnProperty(h)){
2583                             this.initHeader(h, hs[h], false);
2584                         }
2585                     }
2586                 }
2587                 if(options.xmlData){
2588                     this.initHeader('Content-Type', 'text/xml', false);
2589                     method = 'POST';
2590                     data = options.xmlData;
2591                 }
2592             }
2593
2594             return this.asyncRequest(method, uri, cb, data);
2595         },
2596
2597         serializeForm : function(form) {
2598             if(typeof form == 'string') {
2599                 form = (document.getElementById(form) || document.forms[form]);
2600             }
2601
2602             var el, name, val, disabled, data = '', hasSubmit = false;
2603             for (var i = 0; i < form.elements.length; i++) {
2604                 el = form.elements[i];
2605                 disabled = form.elements[i].disabled;
2606                 name = form.elements[i].name;
2607                 val = form.elements[i].value;
2608
2609                 if (!disabled && name){
2610                     switch (el.type)
2611                             {
2612                         case 'select-one':
2613                         case 'select-multiple':
2614                             for (var j = 0; j < el.options.length; j++) {
2615                                 if (el.options[j].selected) {
2616                                     if (Roo.isIE) {
2617                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2618                                     }
2619                                     else {
2620                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2621                                     }
2622                                 }
2623                             }
2624                             break;
2625                         case 'radio':
2626                         case 'checkbox':
2627                             if (el.checked) {
2628                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2629                             }
2630                             break;
2631                         case 'file':
2632
2633                         case undefined:
2634
2635                         case 'reset':
2636
2637                         case 'button':
2638
2639                             break;
2640                         case 'submit':
2641                             if(hasSubmit == false) {
2642                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2643                                 hasSubmit = true;
2644                             }
2645                             break;
2646                         default:
2647                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2648                             break;
2649                     }
2650                 }
2651             }
2652             data = data.substr(0, data.length - 1);
2653             return data;
2654         },
2655
2656         headers:{},
2657
2658         hasHeaders:false,
2659
2660         useDefaultHeader:true,
2661
2662         defaultPostHeader:'application/x-www-form-urlencoded',
2663
2664         useDefaultXhrHeader:true,
2665
2666         defaultXhrHeader:'XMLHttpRequest',
2667
2668         hasDefaultHeaders:true,
2669
2670         defaultHeaders:{},
2671
2672         poll:{},
2673
2674         timeout:{},
2675
2676         pollInterval:50,
2677
2678         transactionId:0,
2679
2680         setProgId:function(id)
2681         {
2682             this.activeX.unshift(id);
2683         },
2684
2685         setDefaultPostHeader:function(b)
2686         {
2687             this.useDefaultHeader = b;
2688         },
2689
2690         setDefaultXhrHeader:function(b)
2691         {
2692             this.useDefaultXhrHeader = b;
2693         },
2694
2695         setPollingInterval:function(i)
2696         {
2697             if (typeof i == 'number' && isFinite(i)) {
2698                 this.pollInterval = i;
2699             }
2700         },
2701
2702         createXhrObject:function(transactionId)
2703         {
2704             var obj,http;
2705             try
2706             {
2707
2708                 http = new XMLHttpRequest();
2709
2710                 obj = { conn:http, tId:transactionId };
2711             }
2712             catch(e)
2713             {
2714                 for (var i = 0; i < this.activeX.length; ++i) {
2715                     try
2716                     {
2717
2718                         http = new ActiveXObject(this.activeX[i]);
2719
2720                         obj = { conn:http, tId:transactionId };
2721                         break;
2722                     }
2723                     catch(e) {
2724                     }
2725                 }
2726             }
2727             finally
2728             {
2729                 return obj;
2730             }
2731         },
2732
2733         getConnectionObject:function()
2734         {
2735             var o;
2736             var tId = this.transactionId;
2737
2738             try
2739             {
2740                 o = this.createXhrObject(tId);
2741                 if (o) {
2742                     this.transactionId++;
2743                 }
2744             }
2745             catch(e) {
2746             }
2747             finally
2748             {
2749                 return o;
2750             }
2751         },
2752
2753         asyncRequest:function(method, uri, callback, postData)
2754         {
2755             var o = this.getConnectionObject();
2756
2757             if (!o) {
2758                 return null;
2759             }
2760             else {
2761                 o.conn.open(method, uri, true);
2762
2763                 if (this.useDefaultXhrHeader) {
2764                     if (!this.defaultHeaders['X-Requested-With']) {
2765                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2766                     }
2767                 }
2768
2769                 if(postData && this.useDefaultHeader){
2770                     this.initHeader('Content-Type', this.defaultPostHeader);
2771                 }
2772
2773                  if (this.hasDefaultHeaders || this.hasHeaders) {
2774                     this.setHeader(o);
2775                 }
2776
2777                 this.handleReadyState(o, callback);
2778                 o.conn.send(postData || null);
2779
2780                 return o;
2781             }
2782         },
2783
2784         handleReadyState:function(o, callback)
2785         {
2786             var oConn = this;
2787
2788             if (callback && callback.timeout) {
2789                 
2790                 this.timeout[o.tId] = window.setTimeout(function() {
2791                     oConn.abort(o, callback, true);
2792                 }, callback.timeout);
2793             }
2794
2795             this.poll[o.tId] = window.setInterval(
2796                     function() {
2797                         if (o.conn && o.conn.readyState == 4) {
2798                             window.clearInterval(oConn.poll[o.tId]);
2799                             delete oConn.poll[o.tId];
2800
2801                             if(callback && callback.timeout) {
2802                                 window.clearTimeout(oConn.timeout[o.tId]);
2803                                 delete oConn.timeout[o.tId];
2804                             }
2805
2806                             oConn.handleTransactionResponse(o, callback);
2807                         }
2808                     }
2809                     , this.pollInterval);
2810         },
2811
2812         handleTransactionResponse:function(o, callback, isAbort)
2813         {
2814
2815             if (!callback) {
2816                 this.releaseObject(o);
2817                 return;
2818             }
2819
2820             var httpStatus, responseObject;
2821
2822             try
2823             {
2824                 if (o.conn.status !== undefined && o.conn.status != 0) {
2825                     httpStatus = o.conn.status;
2826                 }
2827                 else {
2828                     httpStatus = 13030;
2829                 }
2830             }
2831             catch(e) {
2832
2833
2834                 httpStatus = 13030;
2835             }
2836
2837             if (httpStatus >= 200 && httpStatus < 300) {
2838                 responseObject = this.createResponseObject(o, callback.argument);
2839                 if (callback.success) {
2840                     if (!callback.scope) {
2841                         callback.success(responseObject);
2842                     }
2843                     else {
2844
2845
2846                         callback.success.apply(callback.scope, [responseObject]);
2847                     }
2848                 }
2849             }
2850             else {
2851                 switch (httpStatus) {
2852
2853                     case 12002:
2854                     case 12029:
2855                     case 12030:
2856                     case 12031:
2857                     case 12152:
2858                     case 13030:
2859                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2860                         if (callback.failure) {
2861                             if (!callback.scope) {
2862                                 callback.failure(responseObject);
2863                             }
2864                             else {
2865                                 callback.failure.apply(callback.scope, [responseObject]);
2866                             }
2867                         }
2868                         break;
2869                     default:
2870                         responseObject = this.createResponseObject(o, callback.argument);
2871                         if (callback.failure) {
2872                             if (!callback.scope) {
2873                                 callback.failure(responseObject);
2874                             }
2875                             else {
2876                                 callback.failure.apply(callback.scope, [responseObject]);
2877                             }
2878                         }
2879                 }
2880             }
2881
2882             this.releaseObject(o);
2883             responseObject = null;
2884         },
2885
2886         createResponseObject:function(o, callbackArg)
2887         {
2888             var obj = {};
2889             var headerObj = {};
2890
2891             try
2892             {
2893                 var headerStr = o.conn.getAllResponseHeaders();
2894                 var header = headerStr.split('\n');
2895                 for (var i = 0; i < header.length; i++) {
2896                     var delimitPos = header[i].indexOf(':');
2897                     if (delimitPos != -1) {
2898                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2899                     }
2900                 }
2901             }
2902             catch(e) {
2903             }
2904
2905             obj.tId = o.tId;
2906             obj.status = o.conn.status;
2907             obj.statusText = o.conn.statusText;
2908             obj.getResponseHeader = headerObj;
2909             obj.getAllResponseHeaders = headerStr;
2910             obj.responseText = o.conn.responseText;
2911             obj.responseXML = o.conn.responseXML;
2912
2913             if (typeof callbackArg !== undefined) {
2914                 obj.argument = callbackArg;
2915             }
2916
2917             return obj;
2918         },
2919
2920         createExceptionObject:function(tId, callbackArg, isAbort)
2921         {
2922             var COMM_CODE = 0;
2923             var COMM_ERROR = 'communication failure';
2924             var ABORT_CODE = -1;
2925             var ABORT_ERROR = 'transaction aborted';
2926
2927             var obj = {};
2928
2929             obj.tId = tId;
2930             if (isAbort) {
2931                 obj.status = ABORT_CODE;
2932                 obj.statusText = ABORT_ERROR;
2933             }
2934             else {
2935                 obj.status = COMM_CODE;
2936                 obj.statusText = COMM_ERROR;
2937             }
2938
2939             if (callbackArg) {
2940                 obj.argument = callbackArg;
2941             }
2942
2943             return obj;
2944         },
2945
2946         initHeader:function(label, value, isDefault)
2947         {
2948             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2949
2950             if (headerObj[label] === undefined) {
2951                 headerObj[label] = value;
2952             }
2953             else {
2954
2955
2956                 headerObj[label] = value + "," + headerObj[label];
2957             }
2958
2959             if (isDefault) {
2960                 this.hasDefaultHeaders = true;
2961             }
2962             else {
2963                 this.hasHeaders = true;
2964             }
2965         },
2966
2967
2968         setHeader:function(o)
2969         {
2970             if (this.hasDefaultHeaders) {
2971                 for (var prop in this.defaultHeaders) {
2972                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2973                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2974                     }
2975                 }
2976             }
2977
2978             if (this.hasHeaders) {
2979                 for (var prop in this.headers) {
2980                     if (this.headers.hasOwnProperty(prop)) {
2981                         o.conn.setRequestHeader(prop, this.headers[prop]);
2982                     }
2983                 }
2984                 this.headers = {};
2985                 this.hasHeaders = false;
2986             }
2987         },
2988
2989         resetDefaultHeaders:function() {
2990             delete this.defaultHeaders;
2991             this.defaultHeaders = {};
2992             this.hasDefaultHeaders = false;
2993         },
2994
2995         abort:function(o, callback, isTimeout)
2996         {
2997             if(this.isCallInProgress(o)) {
2998                 o.conn.abort();
2999                 window.clearInterval(this.poll[o.tId]);
3000                 delete this.poll[o.tId];
3001                 if (isTimeout) {
3002                     delete this.timeout[o.tId];
3003                 }
3004
3005                 this.handleTransactionResponse(o, callback, true);
3006
3007                 return true;
3008             }
3009             else {
3010                 return false;
3011             }
3012         },
3013
3014
3015         isCallInProgress:function(o)
3016         {
3017             if (o && o.conn) {
3018                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3019             }
3020             else {
3021
3022                 return false;
3023             }
3024         },
3025
3026
3027         releaseObject:function(o)
3028         {
3029
3030             o.conn = null;
3031
3032             o = null;
3033         },
3034
3035         activeX:[
3036         'MSXML2.XMLHTTP.3.0',
3037         'MSXML2.XMLHTTP',
3038         'Microsoft.XMLHTTP'
3039         ]
3040
3041
3042     };
3043 })();/*
3044  * Portions of this file are based on pieces of Yahoo User Interface Library
3045  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3046  * YUI licensed under the BSD License:
3047  * http://developer.yahoo.net/yui/license.txt
3048  * <script type="text/javascript">
3049  *
3050  */
3051
3052 Roo.lib.Region = function(t, r, b, l) {
3053     this.top = t;
3054     this[1] = t;
3055     this.right = r;
3056     this.bottom = b;
3057     this.left = l;
3058     this[0] = l;
3059 };
3060
3061
3062 Roo.lib.Region.prototype = {
3063     contains : function(region) {
3064         return ( region.left >= this.left &&
3065                  region.right <= this.right &&
3066                  region.top >= this.top &&
3067                  region.bottom <= this.bottom    );
3068
3069     },
3070
3071     getArea : function() {
3072         return ( (this.bottom - this.top) * (this.right - this.left) );
3073     },
3074
3075     intersect : function(region) {
3076         var t = Math.max(this.top, region.top);
3077         var r = Math.min(this.right, region.right);
3078         var b = Math.min(this.bottom, region.bottom);
3079         var l = Math.max(this.left, region.left);
3080
3081         if (b >= t && r >= l) {
3082             return new Roo.lib.Region(t, r, b, l);
3083         } else {
3084             return null;
3085         }
3086     },
3087     union : function(region) {
3088         var t = Math.min(this.top, region.top);
3089         var r = Math.max(this.right, region.right);
3090         var b = Math.max(this.bottom, region.bottom);
3091         var l = Math.min(this.left, region.left);
3092
3093         return new Roo.lib.Region(t, r, b, l);
3094     },
3095
3096     adjust : function(t, l, b, r) {
3097         this.top += t;
3098         this.left += l;
3099         this.right += r;
3100         this.bottom += b;
3101         return this;
3102     }
3103 };
3104
3105 Roo.lib.Region.getRegion = function(el) {
3106     var p = Roo.lib.Dom.getXY(el);
3107
3108     var t = p[1];
3109     var r = p[0] + el.offsetWidth;
3110     var b = p[1] + el.offsetHeight;
3111     var l = p[0];
3112
3113     return new Roo.lib.Region(t, r, b, l);
3114 };
3115 /*
3116  * Portions of this file are based on pieces of Yahoo User Interface Library
3117  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3118  * YUI licensed under the BSD License:
3119  * http://developer.yahoo.net/yui/license.txt
3120  * <script type="text/javascript">
3121  *
3122  */
3123 //@@dep Roo.lib.Region
3124
3125
3126 Roo.lib.Point = function(x, y) {
3127     if (x instanceof Array) {
3128         y = x[1];
3129         x = x[0];
3130     }
3131     this.x = this.right = this.left = this[0] = x;
3132     this.y = this.top = this.bottom = this[1] = y;
3133 };
3134
3135 Roo.lib.Point.prototype = new Roo.lib.Region();
3136 /*
3137  * Portions of this file are based on pieces of Yahoo User Interface Library
3138  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3139  * YUI licensed under the BSD License:
3140  * http://developer.yahoo.net/yui/license.txt
3141  * <script type="text/javascript">
3142  *
3143  */
3144  
3145 (function() {   
3146
3147     Roo.lib.Anim = {
3148         scroll : function(el, args, duration, easing, cb, scope) {
3149             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3150         },
3151
3152         motion : function(el, args, duration, easing, cb, scope) {
3153             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3154         },
3155
3156         color : function(el, args, duration, easing, cb, scope) {
3157             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3158         },
3159
3160         run : function(el, args, duration, easing, cb, scope, type) {
3161             type = type || Roo.lib.AnimBase;
3162             if (typeof easing == "string") {
3163                 easing = Roo.lib.Easing[easing];
3164             }
3165             var anim = new type(el, args, duration, easing);
3166             anim.animateX(function() {
3167                 Roo.callback(cb, scope);
3168             });
3169             return anim;
3170         }
3171     };
3172 })();/*
3173  * Portions of this file are based on pieces of Yahoo User Interface Library
3174  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3175  * YUI licensed under the BSD License:
3176  * http://developer.yahoo.net/yui/license.txt
3177  * <script type="text/javascript">
3178  *
3179  */
3180
3181 (function() {    
3182     var libFlyweight;
3183     
3184     function fly(el) {
3185         if (!libFlyweight) {
3186             libFlyweight = new Roo.Element.Flyweight();
3187         }
3188         libFlyweight.dom = el;
3189         return libFlyweight;
3190     }
3191
3192     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3193     
3194    
3195     
3196     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3197         if (el) {
3198             this.init(el, attributes, duration, method);
3199         }
3200     };
3201
3202     Roo.lib.AnimBase.fly = fly;
3203     
3204     
3205     
3206     Roo.lib.AnimBase.prototype = {
3207
3208         toString: function() {
3209             var el = this.getEl();
3210             var id = el.id || el.tagName;
3211             return ("Anim " + id);
3212         },
3213
3214         patterns: {
3215             noNegatives:        /width|height|opacity|padding/i,
3216             offsetAttribute:  /^((width|height)|(top|left))$/,
3217             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3218             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3219         },
3220
3221
3222         doMethod: function(attr, start, end) {
3223             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3224         },
3225
3226
3227         setAttribute: function(attr, val, unit) {
3228             if (this.patterns.noNegatives.test(attr)) {
3229                 val = (val > 0) ? val : 0;
3230             }
3231
3232             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3233         },
3234
3235
3236         getAttribute: function(attr) {
3237             var el = this.getEl();
3238             var val = fly(el).getStyle(attr);
3239
3240             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3241                 return parseFloat(val);
3242             }
3243
3244             var a = this.patterns.offsetAttribute.exec(attr) || [];
3245             var pos = !!( a[3] );
3246             var box = !!( a[2] );
3247
3248
3249             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3250                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3251             } else {
3252                 val = 0;
3253             }
3254
3255             return val;
3256         },
3257
3258
3259         getDefaultUnit: function(attr) {
3260             if (this.patterns.defaultUnit.test(attr)) {
3261                 return 'px';
3262             }
3263
3264             return '';
3265         },
3266
3267         animateX : function(callback, scope) {
3268             var f = function() {
3269                 this.onComplete.removeListener(f);
3270                 if (typeof callback == "function") {
3271                     callback.call(scope || this, this);
3272                 }
3273             };
3274             this.onComplete.addListener(f, this);
3275             this.animate();
3276         },
3277
3278
3279         setRuntimeAttribute: function(attr) {
3280             var start;
3281             var end;
3282             var attributes = this.attributes;
3283
3284             this.runtimeAttributes[attr] = {};
3285
3286             var isset = function(prop) {
3287                 return (typeof prop !== 'undefined');
3288             };
3289
3290             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3291                 return false;
3292             }
3293
3294             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3295
3296
3297             if (isset(attributes[attr]['to'])) {
3298                 end = attributes[attr]['to'];
3299             } else if (isset(attributes[attr]['by'])) {
3300                 if (start.constructor == Array) {
3301                     end = [];
3302                     for (var i = 0, len = start.length; i < len; ++i) {
3303                         end[i] = start[i] + attributes[attr]['by'][i];
3304                     }
3305                 } else {
3306                     end = start + attributes[attr]['by'];
3307                 }
3308             }
3309
3310             this.runtimeAttributes[attr].start = start;
3311             this.runtimeAttributes[attr].end = end;
3312
3313
3314             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3315         },
3316
3317
3318         init: function(el, attributes, duration, method) {
3319
3320             var isAnimated = false;
3321
3322
3323             var startTime = null;
3324
3325
3326             var actualFrames = 0;
3327
3328
3329             el = Roo.getDom(el);
3330
3331
3332             this.attributes = attributes || {};
3333
3334
3335             this.duration = duration || 1;
3336
3337
3338             this.method = method || Roo.lib.Easing.easeNone;
3339
3340
3341             this.useSeconds = true;
3342
3343
3344             this.currentFrame = 0;
3345
3346
3347             this.totalFrames = Roo.lib.AnimMgr.fps;
3348
3349
3350             this.getEl = function() {
3351                 return el;
3352             };
3353
3354
3355             this.isAnimated = function() {
3356                 return isAnimated;
3357             };
3358
3359
3360             this.getStartTime = function() {
3361                 return startTime;
3362             };
3363
3364             this.runtimeAttributes = {};
3365
3366
3367             this.animate = function() {
3368                 if (this.isAnimated()) {
3369                     return false;
3370                 }
3371
3372                 this.currentFrame = 0;
3373
3374                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3375
3376                 Roo.lib.AnimMgr.registerElement(this);
3377             };
3378
3379
3380             this.stop = function(finish) {
3381                 if (finish) {
3382                     this.currentFrame = this.totalFrames;
3383                     this._onTween.fire();
3384                 }
3385                 Roo.lib.AnimMgr.stop(this);
3386             };
3387
3388             var onStart = function() {
3389                 this.onStart.fire();
3390
3391                 this.runtimeAttributes = {};
3392                 for (var attr in this.attributes) {
3393                     this.setRuntimeAttribute(attr);
3394                 }
3395
3396                 isAnimated = true;
3397                 actualFrames = 0;
3398                 startTime = new Date();
3399             };
3400
3401
3402             var onTween = function() {
3403                 var data = {
3404                     duration: new Date() - this.getStartTime(),
3405                     currentFrame: this.currentFrame
3406                 };
3407
3408                 data.toString = function() {
3409                     return (
3410                             'duration: ' + data.duration +
3411                             ', currentFrame: ' + data.currentFrame
3412                             );
3413                 };
3414
3415                 this.onTween.fire(data);
3416
3417                 var runtimeAttributes = this.runtimeAttributes;
3418
3419                 for (var attr in runtimeAttributes) {
3420                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3421                 }
3422
3423                 actualFrames += 1;
3424             };
3425
3426             var onComplete = function() {
3427                 var actual_duration = (new Date() - startTime) / 1000 ;
3428
3429                 var data = {
3430                     duration: actual_duration,
3431                     frames: actualFrames,
3432                     fps: actualFrames / actual_duration
3433                 };
3434
3435                 data.toString = function() {
3436                     return (
3437                             'duration: ' + data.duration +
3438                             ', frames: ' + data.frames +
3439                             ', fps: ' + data.fps
3440                             );
3441                 };
3442
3443                 isAnimated = false;
3444                 actualFrames = 0;
3445                 this.onComplete.fire(data);
3446             };
3447
3448
3449             this._onStart = new Roo.util.Event(this);
3450             this.onStart = new Roo.util.Event(this);
3451             this.onTween = new Roo.util.Event(this);
3452             this._onTween = new Roo.util.Event(this);
3453             this.onComplete = new Roo.util.Event(this);
3454             this._onComplete = new Roo.util.Event(this);
3455             this._onStart.addListener(onStart);
3456             this._onTween.addListener(onTween);
3457             this._onComplete.addListener(onComplete);
3458         }
3459     };
3460 })();
3461 /*
3462  * Portions of this file are based on pieces of Yahoo User Interface Library
3463  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3464  * YUI licensed under the BSD License:
3465  * http://developer.yahoo.net/yui/license.txt
3466  * <script type="text/javascript">
3467  *
3468  */
3469
3470 Roo.lib.AnimMgr = new function() {
3471
3472     var thread = null;
3473
3474
3475     var queue = [];
3476
3477
3478     var tweenCount = 0;
3479
3480
3481     this.fps = 1000;
3482
3483
3484     this.delay = 1;
3485
3486
3487     this.registerElement = function(tween) {
3488         queue[queue.length] = tween;
3489         tweenCount += 1;
3490         tween._onStart.fire();
3491         this.start();
3492     };
3493
3494
3495     this.unRegister = function(tween, index) {
3496         tween._onComplete.fire();
3497         index = index || getIndex(tween);
3498         if (index != -1) {
3499             queue.splice(index, 1);
3500         }
3501
3502         tweenCount -= 1;
3503         if (tweenCount <= 0) {
3504             this.stop();
3505         }
3506     };
3507
3508
3509     this.start = function() {
3510         if (thread === null) {
3511             thread = setInterval(this.run, this.delay);
3512         }
3513     };
3514
3515
3516     this.stop = function(tween) {
3517         if (!tween) {
3518             clearInterval(thread);
3519
3520             for (var i = 0, len = queue.length; i < len; ++i) {
3521                 if (queue[0].isAnimated()) {
3522                     this.unRegister(queue[0], 0);
3523                 }
3524             }
3525
3526             queue = [];
3527             thread = null;
3528             tweenCount = 0;
3529         }
3530         else {
3531             this.unRegister(tween);
3532         }
3533     };
3534
3535
3536     this.run = function() {
3537         for (var i = 0, len = queue.length; i < len; ++i) {
3538             var tween = queue[i];
3539             if (!tween || !tween.isAnimated()) {
3540                 continue;
3541             }
3542
3543             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3544             {
3545                 tween.currentFrame += 1;
3546
3547                 if (tween.useSeconds) {
3548                     correctFrame(tween);
3549                 }
3550                 tween._onTween.fire();
3551             }
3552             else {
3553                 Roo.lib.AnimMgr.stop(tween, i);
3554             }
3555         }
3556     };
3557
3558     var getIndex = function(anim) {
3559         for (var i = 0, len = queue.length; i < len; ++i) {
3560             if (queue[i] == anim) {
3561                 return i;
3562             }
3563         }
3564         return -1;
3565     };
3566
3567
3568     var correctFrame = function(tween) {
3569         var frames = tween.totalFrames;
3570         var frame = tween.currentFrame;
3571         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3572         var elapsed = (new Date() - tween.getStartTime());
3573         var tweak = 0;
3574
3575         if (elapsed < tween.duration * 1000) {
3576             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3577         } else {
3578             tweak = frames - (frame + 1);
3579         }
3580         if (tweak > 0 && isFinite(tweak)) {
3581             if (tween.currentFrame + tweak >= frames) {
3582                 tweak = frames - (frame + 1);
3583             }
3584
3585             tween.currentFrame += tweak;
3586         }
3587     };
3588 };
3589
3590     /*
3591  * Portions of this file are based on pieces of Yahoo User Interface Library
3592  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3593  * YUI licensed under the BSD License:
3594  * http://developer.yahoo.net/yui/license.txt
3595  * <script type="text/javascript">
3596  *
3597  */
3598 Roo.lib.Bezier = new function() {
3599
3600         this.getPosition = function(points, t) {
3601             var n = points.length;
3602             var tmp = [];
3603
3604             for (var i = 0; i < n; ++i) {
3605                 tmp[i] = [points[i][0], points[i][1]];
3606             }
3607
3608             for (var j = 1; j < n; ++j) {
3609                 for (i = 0; i < n - j; ++i) {
3610                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3611                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3612                 }
3613             }
3614
3615             return [ tmp[0][0], tmp[0][1] ];
3616
3617         };
3618     };/*
3619  * Portions of this file are based on pieces of Yahoo User Interface Library
3620  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3621  * YUI licensed under the BSD License:
3622  * http://developer.yahoo.net/yui/license.txt
3623  * <script type="text/javascript">
3624  *
3625  */
3626 (function() {
3627
3628     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3629         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3630     };
3631
3632     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3633
3634     var fly = Roo.lib.AnimBase.fly;
3635     var Y = Roo.lib;
3636     var superclass = Y.ColorAnim.superclass;
3637     var proto = Y.ColorAnim.prototype;
3638
3639     proto.toString = function() {
3640         var el = this.getEl();
3641         var id = el.id || el.tagName;
3642         return ("ColorAnim " + id);
3643     };
3644
3645     proto.patterns.color = /color$/i;
3646     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3647     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3648     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3649     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3650
3651
3652     proto.parseColor = function(s) {
3653         if (s.length == 3) {
3654             return s;
3655         }
3656
3657         var c = this.patterns.hex.exec(s);
3658         if (c && c.length == 4) {
3659             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3660         }
3661
3662         c = this.patterns.rgb.exec(s);
3663         if (c && c.length == 4) {
3664             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3665         }
3666
3667         c = this.patterns.hex3.exec(s);
3668         if (c && c.length == 4) {
3669             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3670         }
3671
3672         return null;
3673     };
3674     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3675     proto.getAttribute = function(attr) {
3676         var el = this.getEl();
3677         if (this.patterns.color.test(attr)) {
3678             var val = fly(el).getStyle(attr);
3679
3680             if (this.patterns.transparent.test(val)) {
3681                 var parent = el.parentNode;
3682                 val = fly(parent).getStyle(attr);
3683
3684                 while (parent && this.patterns.transparent.test(val)) {
3685                     parent = parent.parentNode;
3686                     val = fly(parent).getStyle(attr);
3687                     if (parent.tagName.toUpperCase() == 'HTML') {
3688                         val = '#fff';
3689                     }
3690                 }
3691             }
3692         } else {
3693             val = superclass.getAttribute.call(this, attr);
3694         }
3695
3696         return val;
3697     };
3698     proto.getAttribute = function(attr) {
3699         var el = this.getEl();
3700         if (this.patterns.color.test(attr)) {
3701             var val = fly(el).getStyle(attr);
3702
3703             if (this.patterns.transparent.test(val)) {
3704                 var parent = el.parentNode;
3705                 val = fly(parent).getStyle(attr);
3706
3707                 while (parent && this.patterns.transparent.test(val)) {
3708                     parent = parent.parentNode;
3709                     val = fly(parent).getStyle(attr);
3710                     if (parent.tagName.toUpperCase() == 'HTML') {
3711                         val = '#fff';
3712                     }
3713                 }
3714             }
3715         } else {
3716             val = superclass.getAttribute.call(this, attr);
3717         }
3718
3719         return val;
3720     };
3721
3722     proto.doMethod = function(attr, start, end) {
3723         var val;
3724
3725         if (this.patterns.color.test(attr)) {
3726             val = [];
3727             for (var i = 0, len = start.length; i < len; ++i) {
3728                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3729             }
3730
3731             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3732         }
3733         else {
3734             val = superclass.doMethod.call(this, attr, start, end);
3735         }
3736
3737         return val;
3738     };
3739
3740     proto.setRuntimeAttribute = function(attr) {
3741         superclass.setRuntimeAttribute.call(this, attr);
3742
3743         if (this.patterns.color.test(attr)) {
3744             var attributes = this.attributes;
3745             var start = this.parseColor(this.runtimeAttributes[attr].start);
3746             var end = this.parseColor(this.runtimeAttributes[attr].end);
3747
3748             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3749                 end = this.parseColor(attributes[attr].by);
3750
3751                 for (var i = 0, len = start.length; i < len; ++i) {
3752                     end[i] = start[i] + end[i];
3753                 }
3754             }
3755
3756             this.runtimeAttributes[attr].start = start;
3757             this.runtimeAttributes[attr].end = end;
3758         }
3759     };
3760 })();
3761
3762 /*
3763  * Portions of this file are based on pieces of Yahoo User Interface Library
3764  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3765  * YUI licensed under the BSD License:
3766  * http://developer.yahoo.net/yui/license.txt
3767  * <script type="text/javascript">
3768  *
3769  */
3770 Roo.lib.Easing = {
3771
3772
3773     easeNone: function (t, b, c, d) {
3774         return c * t / d + b;
3775     },
3776
3777
3778     easeIn: function (t, b, c, d) {
3779         return c * (t /= d) * t + b;
3780     },
3781
3782
3783     easeOut: function (t, b, c, d) {
3784         return -c * (t /= d) * (t - 2) + b;
3785     },
3786
3787
3788     easeBoth: function (t, b, c, d) {
3789         if ((t /= d / 2) < 1) {
3790             return c / 2 * t * t + b;
3791         }
3792
3793         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3794     },
3795
3796
3797     easeInStrong: function (t, b, c, d) {
3798         return c * (t /= d) * t * t * t + b;
3799     },
3800
3801
3802     easeOutStrong: function (t, b, c, d) {
3803         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3804     },
3805
3806
3807     easeBothStrong: function (t, b, c, d) {
3808         if ((t /= d / 2) < 1) {
3809             return c / 2 * t * t * t * t + b;
3810         }
3811
3812         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3813     },
3814
3815
3816
3817     elasticIn: function (t, b, c, d, a, p) {
3818         if (t == 0) {
3819             return b;
3820         }
3821         if ((t /= d) == 1) {
3822             return b + c;
3823         }
3824         if (!p) {
3825             p = d * .3;
3826         }
3827
3828         if (!a || a < Math.abs(c)) {
3829             a = c;
3830             var s = p / 4;
3831         }
3832         else {
3833             var s = p / (2 * Math.PI) * Math.asin(c / a);
3834         }
3835
3836         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3837     },
3838
3839
3840     elasticOut: function (t, b, c, d, a, p) {
3841         if (t == 0) {
3842             return b;
3843         }
3844         if ((t /= d) == 1) {
3845             return b + c;
3846         }
3847         if (!p) {
3848             p = d * .3;
3849         }
3850
3851         if (!a || a < Math.abs(c)) {
3852             a = c;
3853             var s = p / 4;
3854         }
3855         else {
3856             var s = p / (2 * Math.PI) * Math.asin(c / a);
3857         }
3858
3859         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3860     },
3861
3862
3863     elasticBoth: function (t, b, c, d, a, p) {
3864         if (t == 0) {
3865             return b;
3866         }
3867
3868         if ((t /= d / 2) == 2) {
3869             return b + c;
3870         }
3871
3872         if (!p) {
3873             p = d * (.3 * 1.5);
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         if (t < 1) {
3885             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3886                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3887         }
3888         return a * Math.pow(2, -10 * (t -= 1)) *
3889                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3890     },
3891
3892
3893
3894     backIn: function (t, b, c, d, s) {
3895         if (typeof s == 'undefined') {
3896             s = 1.70158;
3897         }
3898         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3899     },
3900
3901
3902     backOut: function (t, b, c, d, s) {
3903         if (typeof s == 'undefined') {
3904             s = 1.70158;
3905         }
3906         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3907     },
3908
3909
3910     backBoth: function (t, b, c, d, s) {
3911         if (typeof s == 'undefined') {
3912             s = 1.70158;
3913         }
3914
3915         if ((t /= d / 2 ) < 1) {
3916             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3917         }
3918         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3919     },
3920
3921
3922     bounceIn: function (t, b, c, d) {
3923         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3924     },
3925
3926
3927     bounceOut: function (t, b, c, d) {
3928         if ((t /= d) < (1 / 2.75)) {
3929             return c * (7.5625 * t * t) + b;
3930         } else if (t < (2 / 2.75)) {
3931             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3932         } else if (t < (2.5 / 2.75)) {
3933             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3934         }
3935         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3936     },
3937
3938
3939     bounceBoth: function (t, b, c, d) {
3940         if (t < d / 2) {
3941             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3942         }
3943         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3944     }
3945 };/*
3946  * Portions of this file are based on pieces of Yahoo User Interface Library
3947  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3948  * YUI licensed under the BSD License:
3949  * http://developer.yahoo.net/yui/license.txt
3950  * <script type="text/javascript">
3951  *
3952  */
3953     (function() {
3954         Roo.lib.Motion = function(el, attributes, duration, method) {
3955             if (el) {
3956                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3957             }
3958         };
3959
3960         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3961
3962
3963         var Y = Roo.lib;
3964         var superclass = Y.Motion.superclass;
3965         var proto = Y.Motion.prototype;
3966
3967         proto.toString = function() {
3968             var el = this.getEl();
3969             var id = el.id || el.tagName;
3970             return ("Motion " + id);
3971         };
3972
3973         proto.patterns.points = /^points$/i;
3974
3975         proto.setAttribute = function(attr, val, unit) {
3976             if (this.patterns.points.test(attr)) {
3977                 unit = unit || 'px';
3978                 superclass.setAttribute.call(this, 'left', val[0], unit);
3979                 superclass.setAttribute.call(this, 'top', val[1], unit);
3980             } else {
3981                 superclass.setAttribute.call(this, attr, val, unit);
3982             }
3983         };
3984
3985         proto.getAttribute = function(attr) {
3986             if (this.patterns.points.test(attr)) {
3987                 var val = [
3988                         superclass.getAttribute.call(this, 'left'),
3989                         superclass.getAttribute.call(this, 'top')
3990                         ];
3991             } else {
3992                 val = superclass.getAttribute.call(this, attr);
3993             }
3994
3995             return val;
3996         };
3997
3998         proto.doMethod = function(attr, start, end) {
3999             var val = null;
4000
4001             if (this.patterns.points.test(attr)) {
4002                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4003                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4004             } else {
4005                 val = superclass.doMethod.call(this, attr, start, end);
4006             }
4007             return val;
4008         };
4009
4010         proto.setRuntimeAttribute = function(attr) {
4011             if (this.patterns.points.test(attr)) {
4012                 var el = this.getEl();
4013                 var attributes = this.attributes;
4014                 var start;
4015                 var control = attributes['points']['control'] || [];
4016                 var end;
4017                 var i, len;
4018
4019                 if (control.length > 0 && !(control[0] instanceof Array)) {
4020                     control = [control];
4021                 } else {
4022                     var tmp = [];
4023                     for (i = 0,len = control.length; i < len; ++i) {
4024                         tmp[i] = control[i];
4025                     }
4026                     control = tmp;
4027                 }
4028
4029                 Roo.fly(el).position();
4030
4031                 if (isset(attributes['points']['from'])) {
4032                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4033                 }
4034                 else {
4035                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4036                 }
4037
4038                 start = this.getAttribute('points');
4039
4040
4041                 if (isset(attributes['points']['to'])) {
4042                     end = translateValues.call(this, attributes['points']['to'], start);
4043
4044                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4045                     for (i = 0,len = control.length; i < len; ++i) {
4046                         control[i] = translateValues.call(this, control[i], start);
4047                     }
4048
4049
4050                 } else if (isset(attributes['points']['by'])) {
4051                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4052
4053                     for (i = 0,len = control.length; i < len; ++i) {
4054                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4055                     }
4056                 }
4057
4058                 this.runtimeAttributes[attr] = [start];
4059
4060                 if (control.length > 0) {
4061                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4062                 }
4063
4064                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4065             }
4066             else {
4067                 superclass.setRuntimeAttribute.call(this, attr);
4068             }
4069         };
4070
4071         var translateValues = function(val, start) {
4072             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4073             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4074
4075             return val;
4076         };
4077
4078         var isset = function(prop) {
4079             return (typeof prop !== 'undefined');
4080         };
4081     })();
4082 /*
4083  * Portions of this file are based on pieces of Yahoo User Interface Library
4084  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4085  * YUI licensed under the BSD License:
4086  * http://developer.yahoo.net/yui/license.txt
4087  * <script type="text/javascript">
4088  *
4089  */
4090     (function() {
4091         Roo.lib.Scroll = function(el, attributes, duration, method) {
4092             if (el) {
4093                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4094             }
4095         };
4096
4097         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4098
4099
4100         var Y = Roo.lib;
4101         var superclass = Y.Scroll.superclass;
4102         var proto = Y.Scroll.prototype;
4103
4104         proto.toString = function() {
4105             var el = this.getEl();
4106             var id = el.id || el.tagName;
4107             return ("Scroll " + id);
4108         };
4109
4110         proto.doMethod = function(attr, start, end) {
4111             var val = null;
4112
4113             if (attr == 'scroll') {
4114                 val = [
4115                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4116                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4117                         ];
4118
4119             } else {
4120                 val = superclass.doMethod.call(this, attr, start, end);
4121             }
4122             return val;
4123         };
4124
4125         proto.getAttribute = function(attr) {
4126             var val = null;
4127             var el = this.getEl();
4128
4129             if (attr == 'scroll') {
4130                 val = [ el.scrollLeft, el.scrollTop ];
4131             } else {
4132                 val = superclass.getAttribute.call(this, attr);
4133             }
4134
4135             return val;
4136         };
4137
4138         proto.setAttribute = function(attr, val, unit) {
4139             var el = this.getEl();
4140
4141             if (attr == 'scroll') {
4142                 el.scrollLeft = val[0];
4143                 el.scrollTop = val[1];
4144             } else {
4145                 superclass.setAttribute.call(this, attr, val, unit);
4146             }
4147         };
4148     })();
4149 /*
4150  * Based on:
4151  * Ext JS Library 1.1.1
4152  * Copyright(c) 2006-2007, Ext JS, LLC.
4153  *
4154  * Originally Released Under LGPL - original licence link has changed is not relivant.
4155  *
4156  * Fork - LGPL
4157  * <script type="text/javascript">
4158  */
4159
4160
4161 // nasty IE9 hack - what a pile of crap that is..
4162
4163  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4164     Range.prototype.createContextualFragment = function (html) {
4165         var doc = window.document;
4166         var container = doc.createElement("div");
4167         container.innerHTML = html;
4168         var frag = doc.createDocumentFragment(), n;
4169         while ((n = container.firstChild)) {
4170             frag.appendChild(n);
4171         }
4172         return frag;
4173     };
4174 }
4175
4176 /**
4177  * @class Roo.DomHelper
4178  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4179  * 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>.
4180  * @singleton
4181  */
4182 Roo.DomHelper = function(){
4183     var tempTableEl = null;
4184     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4185     var tableRe = /^table|tbody|tr|td$/i;
4186     var xmlns = {};
4187     // build as innerHTML where available
4188     /** @ignore */
4189     var createHtml = function(o){
4190         if(typeof o == 'string'){
4191             return o;
4192         }
4193         var b = "";
4194         if(!o.tag){
4195             o.tag = "div";
4196         }
4197         b += "<" + o.tag;
4198         for(var attr in o){
4199             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4200             if(attr == "style"){
4201                 var s = o["style"];
4202                 if(typeof s == "function"){
4203                     s = s.call();
4204                 }
4205                 if(typeof s == "string"){
4206                     b += ' style="' + s + '"';
4207                 }else if(typeof s == "object"){
4208                     b += ' style="';
4209                     for(var key in s){
4210                         if(typeof s[key] != "function"){
4211                             b += key + ":" + s[key] + ";";
4212                         }
4213                     }
4214                     b += '"';
4215                 }
4216             }else{
4217                 if(attr == "cls"){
4218                     b += ' class="' + o["cls"] + '"';
4219                 }else if(attr == "htmlFor"){
4220                     b += ' for="' + o["htmlFor"] + '"';
4221                 }else{
4222                     b += " " + attr + '="' + o[attr] + '"';
4223                 }
4224             }
4225         }
4226         if(emptyTags.test(o.tag)){
4227             b += "/>";
4228         }else{
4229             b += ">";
4230             var cn = o.children || o.cn;
4231             if(cn){
4232                 //http://bugs.kde.org/show_bug.cgi?id=71506
4233                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4234                     for(var i = 0, len = cn.length; i < len; i++) {
4235                         b += createHtml(cn[i], b);
4236                     }
4237                 }else{
4238                     b += createHtml(cn, b);
4239                 }
4240             }
4241             if(o.html){
4242                 b += o.html;
4243             }
4244             b += "</" + o.tag + ">";
4245         }
4246         return b;
4247     };
4248
4249     // build as dom
4250     /** @ignore */
4251     var createDom = function(o, parentNode){
4252          
4253         // defininition craeted..
4254         var ns = false;
4255         if (o.ns && o.ns != 'html') {
4256                
4257             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4258                 xmlns[o.ns] = o.xmlns;
4259                 ns = o.xmlns;
4260             }
4261             if (typeof(xmlns[o.ns]) == 'undefined') {
4262                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4263             }
4264             ns = xmlns[o.ns];
4265         }
4266         
4267         
4268         if (typeof(o) == 'string') {
4269             return parentNode.appendChild(document.createTextNode(o));
4270         }
4271         o.tag = o.tag || div;
4272         if (o.ns && Roo.isIE) {
4273             ns = false;
4274             o.tag = o.ns + ':' + o.tag;
4275             
4276         }
4277         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4278         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4279         for(var attr in o){
4280             
4281             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4282                     attr == "style" || typeof o[attr] == "function") { continue; }
4283                     
4284             if(attr=="cls" && Roo.isIE){
4285                 el.className = o["cls"];
4286             }else{
4287                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4288                 else { 
4289                     el[attr] = o[attr];
4290                 }
4291             }
4292         }
4293         Roo.DomHelper.applyStyles(el, o.style);
4294         var cn = o.children || o.cn;
4295         if(cn){
4296             //http://bugs.kde.org/show_bug.cgi?id=71506
4297              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4298                 for(var i = 0, len = cn.length; i < len; i++) {
4299                     createDom(cn[i], el);
4300                 }
4301             }else{
4302                 createDom(cn, el);
4303             }
4304         }
4305         if(o.html){
4306             el.innerHTML = o.html;
4307         }
4308         if(parentNode){
4309            parentNode.appendChild(el);
4310         }
4311         return el;
4312     };
4313
4314     var ieTable = function(depth, s, h, e){
4315         tempTableEl.innerHTML = [s, h, e].join('');
4316         var i = -1, el = tempTableEl;
4317         while(++i < depth){
4318             el = el.firstChild;
4319         }
4320         return el;
4321     };
4322
4323     // kill repeat to save bytes
4324     var ts = '<table>',
4325         te = '</table>',
4326         tbs = ts+'<tbody>',
4327         tbe = '</tbody>'+te,
4328         trs = tbs + '<tr>',
4329         tre = '</tr>'+tbe;
4330
4331     /**
4332      * @ignore
4333      * Nasty code for IE's broken table implementation
4334      */
4335     var insertIntoTable = function(tag, where, el, html){
4336         if(!tempTableEl){
4337             tempTableEl = document.createElement('div');
4338         }
4339         var node;
4340         var before = null;
4341         if(tag == 'td'){
4342             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4343                 return;
4344             }
4345             if(where == 'beforebegin'){
4346                 before = el;
4347                 el = el.parentNode;
4348             } else{
4349                 before = el.nextSibling;
4350                 el = el.parentNode;
4351             }
4352             node = ieTable(4, trs, html, tre);
4353         }
4354         else if(tag == 'tr'){
4355             if(where == 'beforebegin'){
4356                 before = el;
4357                 el = el.parentNode;
4358                 node = ieTable(3, tbs, html, tbe);
4359             } else if(where == 'afterend'){
4360                 before = el.nextSibling;
4361                 el = el.parentNode;
4362                 node = ieTable(3, tbs, html, tbe);
4363             } else{ // INTO a TR
4364                 if(where == 'afterbegin'){
4365                     before = el.firstChild;
4366                 }
4367                 node = ieTable(4, trs, html, tre);
4368             }
4369         } else if(tag == 'tbody'){
4370             if(where == 'beforebegin'){
4371                 before = el;
4372                 el = el.parentNode;
4373                 node = ieTable(2, ts, html, te);
4374             } else if(where == 'afterend'){
4375                 before = el.nextSibling;
4376                 el = el.parentNode;
4377                 node = ieTable(2, ts, html, te);
4378             } else{
4379                 if(where == 'afterbegin'){
4380                     before = el.firstChild;
4381                 }
4382                 node = ieTable(3, tbs, html, tbe);
4383             }
4384         } else{ // TABLE
4385             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4386                 return;
4387             }
4388             if(where == 'afterbegin'){
4389                 before = el.firstChild;
4390             }
4391             node = ieTable(2, ts, html, te);
4392         }
4393         el.insertBefore(node, before);
4394         return node;
4395     };
4396
4397     return {
4398     /** True to force the use of DOM instead of html fragments @type Boolean */
4399     useDom : false,
4400
4401     /**
4402      * Returns the markup for the passed Element(s) config
4403      * @param {Object} o The Dom object spec (and children)
4404      * @return {String}
4405      */
4406     markup : function(o){
4407         return createHtml(o);
4408     },
4409
4410     /**
4411      * Applies a style specification to an element
4412      * @param {String/HTMLElement} el The element to apply styles to
4413      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4414      * a function which returns such a specification.
4415      */
4416     applyStyles : function(el, styles){
4417         if(styles){
4418            el = Roo.fly(el);
4419            if(typeof styles == "string"){
4420                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4421                var matches;
4422                while ((matches = re.exec(styles)) != null){
4423                    el.setStyle(matches[1], matches[2]);
4424                }
4425            }else if (typeof styles == "object"){
4426                for (var style in styles){
4427                   el.setStyle(style, styles[style]);
4428                }
4429            }else if (typeof styles == "function"){
4430                 Roo.DomHelper.applyStyles(el, styles.call());
4431            }
4432         }
4433     },
4434
4435     /**
4436      * Inserts an HTML fragment into the Dom
4437      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4438      * @param {HTMLElement} el The context element
4439      * @param {String} html The HTML fragmenet
4440      * @return {HTMLElement} The new node
4441      */
4442     insertHtml : function(where, el, html){
4443         where = where.toLowerCase();
4444         if(el.insertAdjacentHTML){
4445             if(tableRe.test(el.tagName)){
4446                 var rs;
4447                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4448                     return rs;
4449                 }
4450             }
4451             switch(where){
4452                 case "beforebegin":
4453                     el.insertAdjacentHTML('BeforeBegin', html);
4454                     return el.previousSibling;
4455                 case "afterbegin":
4456                     el.insertAdjacentHTML('AfterBegin', html);
4457                     return el.firstChild;
4458                 case "beforeend":
4459                     el.insertAdjacentHTML('BeforeEnd', html);
4460                     return el.lastChild;
4461                 case "afterend":
4462                     el.insertAdjacentHTML('AfterEnd', html);
4463                     return el.nextSibling;
4464             }
4465             throw 'Illegal insertion point -> "' + where + '"';
4466         }
4467         var range = el.ownerDocument.createRange();
4468         var frag;
4469         switch(where){
4470              case "beforebegin":
4471                 range.setStartBefore(el);
4472                 frag = range.createContextualFragment(html);
4473                 el.parentNode.insertBefore(frag, el);
4474                 return el.previousSibling;
4475              case "afterbegin":
4476                 if(el.firstChild){
4477                     range.setStartBefore(el.firstChild);
4478                     frag = range.createContextualFragment(html);
4479                     el.insertBefore(frag, el.firstChild);
4480                     return el.firstChild;
4481                 }else{
4482                     el.innerHTML = html;
4483                     return el.firstChild;
4484                 }
4485             case "beforeend":
4486                 if(el.lastChild){
4487                     range.setStartAfter(el.lastChild);
4488                     frag = range.createContextualFragment(html);
4489                     el.appendChild(frag);
4490                     return el.lastChild;
4491                 }else{
4492                     el.innerHTML = html;
4493                     return el.lastChild;
4494                 }
4495             case "afterend":
4496                 range.setStartAfter(el);
4497                 frag = range.createContextualFragment(html);
4498                 el.parentNode.insertBefore(frag, el.nextSibling);
4499                 return el.nextSibling;
4500             }
4501             throw 'Illegal insertion point -> "' + where + '"';
4502     },
4503
4504     /**
4505      * Creates new Dom element(s) and inserts them before el
4506      * @param {String/HTMLElement/Element} el The context element
4507      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4508      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4509      * @return {HTMLElement/Roo.Element} The new node
4510      */
4511     insertBefore : function(el, o, returnElement){
4512         return this.doInsert(el, o, returnElement, "beforeBegin");
4513     },
4514
4515     /**
4516      * Creates new Dom element(s) and inserts them after el
4517      * @param {String/HTMLElement/Element} el The context element
4518      * @param {Object} o The Dom object spec (and children)
4519      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4520      * @return {HTMLElement/Roo.Element} The new node
4521      */
4522     insertAfter : function(el, o, returnElement){
4523         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4524     },
4525
4526     /**
4527      * Creates new Dom element(s) and inserts them as the first child of el
4528      * @param {String/HTMLElement/Element} el The context element
4529      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4530      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4531      * @return {HTMLElement/Roo.Element} The new node
4532      */
4533     insertFirst : function(el, o, returnElement){
4534         return this.doInsert(el, o, returnElement, "afterBegin");
4535     },
4536
4537     // private
4538     doInsert : function(el, o, returnElement, pos, sibling){
4539         el = Roo.getDom(el);
4540         var newNode;
4541         if(this.useDom || o.ns){
4542             newNode = createDom(o, null);
4543             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4544         }else{
4545             var html = createHtml(o);
4546             newNode = this.insertHtml(pos, el, html);
4547         }
4548         return returnElement ? Roo.get(newNode, true) : newNode;
4549     },
4550
4551     /**
4552      * Creates new Dom element(s) and appends them to el
4553      * @param {String/HTMLElement/Element} el The context element
4554      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4555      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4556      * @return {HTMLElement/Roo.Element} The new node
4557      */
4558     append : function(el, o, returnElement){
4559         el = Roo.getDom(el);
4560         var newNode;
4561         if(this.useDom || o.ns){
4562             newNode = createDom(o, null);
4563             el.appendChild(newNode);
4564         }else{
4565             var html = createHtml(o);
4566             newNode = this.insertHtml("beforeEnd", el, html);
4567         }
4568         return returnElement ? Roo.get(newNode, true) : newNode;
4569     },
4570
4571     /**
4572      * Creates new Dom element(s) and overwrites the contents of el with them
4573      * @param {String/HTMLElement/Element} el The context element
4574      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4575      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4576      * @return {HTMLElement/Roo.Element} The new node
4577      */
4578     overwrite : function(el, o, returnElement){
4579         el = Roo.getDom(el);
4580         if (o.ns) {
4581           
4582             while (el.childNodes.length) {
4583                 el.removeChild(el.firstChild);
4584             }
4585             createDom(o, el);
4586         } else {
4587             el.innerHTML = createHtml(o);   
4588         }
4589         
4590         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4591     },
4592
4593     /**
4594      * Creates a new Roo.DomHelper.Template from the Dom object spec
4595      * @param {Object} o The Dom object spec (and children)
4596      * @return {Roo.DomHelper.Template} The new template
4597      */
4598     createTemplate : function(o){
4599         var html = createHtml(o);
4600         return new Roo.Template(html);
4601     }
4602     };
4603 }();
4604 /*
4605  * Based on:
4606  * Ext JS Library 1.1.1
4607  * Copyright(c) 2006-2007, Ext JS, LLC.
4608  *
4609  * Originally Released Under LGPL - original licence link has changed is not relivant.
4610  *
4611  * Fork - LGPL
4612  * <script type="text/javascript">
4613  */
4614  
4615 /**
4616 * @class Roo.Template
4617 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4618 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4619 * Usage:
4620 <pre><code>
4621 var t = new Roo.Template({
4622     html :  '&lt;div name="{id}"&gt;' + 
4623         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4624         '&lt;/div&gt;',
4625     myformat: function (value, allValues) {
4626         return 'XX' + value;
4627     }
4628 });
4629 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4630 </code></pre>
4631 * For more information see this blog post with examples:
4632 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4633      - Create Elements using DOM, HTML fragments and Templates</a>. 
4634 * @constructor
4635 * @param {Object} cfg - Configuration object.
4636 */
4637 Roo.Template = function(cfg){
4638     // BC!
4639     if(cfg instanceof Array){
4640         cfg = cfg.join("");
4641     }else if(arguments.length > 1){
4642         cfg = Array.prototype.join.call(arguments, "");
4643     }
4644     
4645     
4646     if (typeof(cfg) == 'object') {
4647         Roo.apply(this,cfg)
4648     } else {
4649         // bc
4650         this.html = cfg;
4651     }
4652     if (this.url) {
4653         this.load();
4654     }
4655     
4656 };
4657 Roo.Template.prototype = {
4658     
4659     /**
4660      * @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..
4661      *                    it should be fixed so that template is observable...
4662      */
4663     url : false,
4664     /**
4665      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4666      */
4667     html : '',
4668     /**
4669      * Returns an HTML fragment of this template with the specified values applied.
4670      * @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'})
4671      * @return {String} The HTML fragment
4672      */
4673     applyTemplate : function(values){
4674         try {
4675            
4676             if(this.compiled){
4677                 return this.compiled(values);
4678             }
4679             var useF = this.disableFormats !== true;
4680             var fm = Roo.util.Format, tpl = this;
4681             var fn = function(m, name, format, args){
4682                 if(format && useF){
4683                     if(format.substr(0, 5) == "this."){
4684                         return tpl.call(format.substr(5), values[name], values);
4685                     }else{
4686                         if(args){
4687                             // quoted values are required for strings in compiled templates, 
4688                             // but for non compiled we need to strip them
4689                             // quoted reversed for jsmin
4690                             var re = /^\s*['"](.*)["']\s*$/;
4691                             args = args.split(',');
4692                             for(var i = 0, len = args.length; i < len; i++){
4693                                 args[i] = args[i].replace(re, "$1");
4694                             }
4695                             args = [values[name]].concat(args);
4696                         }else{
4697                             args = [values[name]];
4698                         }
4699                         return fm[format].apply(fm, args);
4700                     }
4701                 }else{
4702                     return values[name] !== undefined ? values[name] : "";
4703                 }
4704             };
4705             return this.html.replace(this.re, fn);
4706         } catch (e) {
4707             Roo.log(e);
4708             throw e;
4709         }
4710          
4711     },
4712     
4713     loading : false,
4714       
4715     load : function ()
4716     {
4717          
4718         if (this.loading) {
4719             return;
4720         }
4721         var _t = this;
4722         
4723         this.loading = true;
4724         this.compiled = false;
4725         
4726         var cx = new Roo.data.Connection();
4727         cx.request({
4728             url : this.url,
4729             method : 'GET',
4730             success : function (response) {
4731                 _t.loading = false;
4732                 _t.html = response.responseText;
4733                 _t.url = false;
4734                 _t.compile();
4735              },
4736             failure : function(response) {
4737                 Roo.log("Template failed to load from " + _t.url);
4738                 _t.loading = false;
4739             }
4740         });
4741     },
4742
4743     /**
4744      * Sets the HTML used as the template and optionally compiles it.
4745      * @param {String} html
4746      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4747      * @return {Roo.Template} this
4748      */
4749     set : function(html, compile){
4750         this.html = html;
4751         this.compiled = null;
4752         if(compile){
4753             this.compile();
4754         }
4755         return this;
4756     },
4757     
4758     /**
4759      * True to disable format functions (defaults to false)
4760      * @type Boolean
4761      */
4762     disableFormats : false,
4763     
4764     /**
4765     * The regular expression used to match template variables 
4766     * @type RegExp
4767     * @property 
4768     */
4769     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4770     
4771     /**
4772      * Compiles the template into an internal function, eliminating the RegEx overhead.
4773      * @return {Roo.Template} this
4774      */
4775     compile : function(){
4776         var fm = Roo.util.Format;
4777         var useF = this.disableFormats !== true;
4778         var sep = Roo.isGecko ? "+" : ",";
4779         var fn = function(m, name, format, args){
4780             if(format && useF){
4781                 args = args ? ',' + args : "";
4782                 if(format.substr(0, 5) != "this."){
4783                     format = "fm." + format + '(';
4784                 }else{
4785                     format = 'this.call("'+ format.substr(5) + '", ';
4786                     args = ", values";
4787                 }
4788             }else{
4789                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4790             }
4791             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4792         };
4793         var body;
4794         // branched to use + in gecko and [].join() in others
4795         if(Roo.isGecko){
4796             body = "this.compiled = function(values){ return '" +
4797                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4798                     "';};";
4799         }else{
4800             body = ["this.compiled = function(values){ return ['"];
4801             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4802             body.push("'].join('');};");
4803             body = body.join('');
4804         }
4805         /**
4806          * eval:var:values
4807          * eval:var:fm
4808          */
4809         eval(body);
4810         return this;
4811     },
4812     
4813     // private function used to call members
4814     call : function(fnName, value, allValues){
4815         return this[fnName](value, allValues);
4816     },
4817     
4818     /**
4819      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4820      * @param {String/HTMLElement/Roo.Element} el The context element
4821      * @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'})
4822      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4823      * @return {HTMLElement/Roo.Element} The new node or Element
4824      */
4825     insertFirst: function(el, values, returnElement){
4826         return this.doInsert('afterBegin', el, values, returnElement);
4827     },
4828
4829     /**
4830      * Applies the supplied values to the template and inserts the new node(s) before el.
4831      * @param {String/HTMLElement/Roo.Element} el The context element
4832      * @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'})
4833      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4834      * @return {HTMLElement/Roo.Element} The new node or Element
4835      */
4836     insertBefore: function(el, values, returnElement){
4837         return this.doInsert('beforeBegin', el, values, returnElement);
4838     },
4839
4840     /**
4841      * Applies the supplied values to the template and inserts the new node(s) after el.
4842      * @param {String/HTMLElement/Roo.Element} el The context element
4843      * @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'})
4844      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4845      * @return {HTMLElement/Roo.Element} The new node or Element
4846      */
4847     insertAfter : function(el, values, returnElement){
4848         return this.doInsert('afterEnd', el, values, returnElement);
4849     },
4850     
4851     /**
4852      * Applies the supplied values to the template and appends the new node(s) to el.
4853      * @param {String/HTMLElement/Roo.Element} el The context element
4854      * @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'})
4855      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4856      * @return {HTMLElement/Roo.Element} The new node or Element
4857      */
4858     append : function(el, values, returnElement){
4859         return this.doInsert('beforeEnd', el, values, returnElement);
4860     },
4861
4862     doInsert : function(where, el, values, returnEl){
4863         el = Roo.getDom(el);
4864         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4865         return returnEl ? Roo.get(newNode, true) : newNode;
4866     },
4867
4868     /**
4869      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4870      * @param {String/HTMLElement/Roo.Element} el The context element
4871      * @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'})
4872      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4873      * @return {HTMLElement/Roo.Element} The new node or Element
4874      */
4875     overwrite : function(el, values, returnElement){
4876         el = Roo.getDom(el);
4877         el.innerHTML = this.applyTemplate(values);
4878         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4879     }
4880 };
4881 /**
4882  * Alias for {@link #applyTemplate}
4883  * @method
4884  */
4885 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4886
4887 // backwards compat
4888 Roo.DomHelper.Template = Roo.Template;
4889
4890 /**
4891  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4892  * @param {String/HTMLElement} el A DOM element or its id
4893  * @returns {Roo.Template} The created template
4894  * @static
4895  */
4896 Roo.Template.from = function(el){
4897     el = Roo.getDom(el);
4898     return new Roo.Template(el.value || el.innerHTML);
4899 };/*
4900  * Based on:
4901  * Ext JS Library 1.1.1
4902  * Copyright(c) 2006-2007, Ext JS, LLC.
4903  *
4904  * Originally Released Under LGPL - original licence link has changed is not relivant.
4905  *
4906  * Fork - LGPL
4907  * <script type="text/javascript">
4908  */
4909  
4910
4911 /*
4912  * This is code is also distributed under MIT license for use
4913  * with jQuery and prototype JavaScript libraries.
4914  */
4915 /**
4916  * @class Roo.DomQuery
4917 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).
4918 <p>
4919 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>
4920
4921 <p>
4922 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.
4923 </p>
4924 <h4>Element Selectors:</h4>
4925 <ul class="list">
4926     <li> <b>*</b> any element</li>
4927     <li> <b>E</b> an element with the tag E</li>
4928     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4929     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4930     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4931     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4932 </ul>
4933 <h4>Attribute Selectors:</h4>
4934 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4935 <ul class="list">
4936     <li> <b>E[foo]</b> has an attribute "foo"</li>
4937     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4938     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4939     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4940     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4941     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4942     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4943 </ul>
4944 <h4>Pseudo Classes:</h4>
4945 <ul class="list">
4946     <li> <b>E:first-child</b> E is the first child of its parent</li>
4947     <li> <b>E:last-child</b> E is the last child of its parent</li>
4948     <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>
4949     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4950     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4951     <li> <b>E:only-child</b> E is the only child of its parent</li>
4952     <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>
4953     <li> <b>E:first</b> the first E in the resultset</li>
4954     <li> <b>E:last</b> the last E in the resultset</li>
4955     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4956     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4957     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4958     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4959     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4960     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4961     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4962     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4963     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4964 </ul>
4965 <h4>CSS Value Selectors:</h4>
4966 <ul class="list">
4967     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4968     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4969     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4970     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4971     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4972     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4973 </ul>
4974  * @singleton
4975  */
4976 Roo.DomQuery = function(){
4977     var cache = {}, simpleCache = {}, valueCache = {};
4978     var nonSpace = /\S/;
4979     var trimRe = /^\s+|\s+$/g;
4980     var tplRe = /\{(\d+)\}/g;
4981     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4982     var tagTokenRe = /^(#)?([\w-\*]+)/;
4983     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4984
4985     function child(p, index){
4986         var i = 0;
4987         var n = p.firstChild;
4988         while(n){
4989             if(n.nodeType == 1){
4990                if(++i == index){
4991                    return n;
4992                }
4993             }
4994             n = n.nextSibling;
4995         }
4996         return null;
4997     };
4998
4999     function next(n){
5000         while((n = n.nextSibling) && n.nodeType != 1);
5001         return n;
5002     };
5003
5004     function prev(n){
5005         while((n = n.previousSibling) && n.nodeType != 1);
5006         return n;
5007     };
5008
5009     function children(d){
5010         var n = d.firstChild, ni = -1;
5011             while(n){
5012                 var nx = n.nextSibling;
5013                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5014                     d.removeChild(n);
5015                 }else{
5016                     n.nodeIndex = ++ni;
5017                 }
5018                 n = nx;
5019             }
5020             return this;
5021         };
5022
5023     function byClassName(c, a, v){
5024         if(!v){
5025             return c;
5026         }
5027         var r = [], ri = -1, cn;
5028         for(var i = 0, ci; ci = c[i]; i++){
5029             if((' '+ci.className+' ').indexOf(v) != -1){
5030                 r[++ri] = ci;
5031             }
5032         }
5033         return r;
5034     };
5035
5036     function attrValue(n, attr){
5037         if(!n.tagName && typeof n.length != "undefined"){
5038             n = n[0];
5039         }
5040         if(!n){
5041             return null;
5042         }
5043         if(attr == "for"){
5044             return n.htmlFor;
5045         }
5046         if(attr == "class" || attr == "className"){
5047             return n.className;
5048         }
5049         return n.getAttribute(attr) || n[attr];
5050
5051     };
5052
5053     function getNodes(ns, mode, tagName){
5054         var result = [], ri = -1, cs;
5055         if(!ns){
5056             return result;
5057         }
5058         tagName = tagName || "*";
5059         if(typeof ns.getElementsByTagName != "undefined"){
5060             ns = [ns];
5061         }
5062         if(!mode){
5063             for(var i = 0, ni; ni = ns[i]; i++){
5064                 cs = ni.getElementsByTagName(tagName);
5065                 for(var j = 0, ci; ci = cs[j]; j++){
5066                     result[++ri] = ci;
5067                 }
5068             }
5069         }else if(mode == "/" || mode == ">"){
5070             var utag = tagName.toUpperCase();
5071             for(var i = 0, ni, cn; ni = ns[i]; i++){
5072                 cn = ni.children || ni.childNodes;
5073                 for(var j = 0, cj; cj = cn[j]; j++){
5074                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5075                         result[++ri] = cj;
5076                     }
5077                 }
5078             }
5079         }else if(mode == "+"){
5080             var utag = tagName.toUpperCase();
5081             for(var i = 0, n; n = ns[i]; i++){
5082                 while((n = n.nextSibling) && n.nodeType != 1);
5083                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5084                     result[++ri] = n;
5085                 }
5086             }
5087         }else if(mode == "~"){
5088             for(var i = 0, n; n = ns[i]; i++){
5089                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5090                 if(n){
5091                     result[++ri] = n;
5092                 }
5093             }
5094         }
5095         return result;
5096     };
5097
5098     function concat(a, b){
5099         if(b.slice){
5100             return a.concat(b);
5101         }
5102         for(var i = 0, l = b.length; i < l; i++){
5103             a[a.length] = b[i];
5104         }
5105         return a;
5106     }
5107
5108     function byTag(cs, tagName){
5109         if(cs.tagName || cs == document){
5110             cs = [cs];
5111         }
5112         if(!tagName){
5113             return cs;
5114         }
5115         var r = [], ri = -1;
5116         tagName = tagName.toLowerCase();
5117         for(var i = 0, ci; ci = cs[i]; i++){
5118             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5119                 r[++ri] = ci;
5120             }
5121         }
5122         return r;
5123     };
5124
5125     function byId(cs, attr, id){
5126         if(cs.tagName || cs == document){
5127             cs = [cs];
5128         }
5129         if(!id){
5130             return cs;
5131         }
5132         var r = [], ri = -1;
5133         for(var i = 0,ci; ci = cs[i]; i++){
5134             if(ci && ci.id == id){
5135                 r[++ri] = ci;
5136                 return r;
5137             }
5138         }
5139         return r;
5140     };
5141
5142     function byAttribute(cs, attr, value, op, custom){
5143         var r = [], ri = -1, st = custom=="{";
5144         var f = Roo.DomQuery.operators[op];
5145         for(var i = 0, ci; ci = cs[i]; i++){
5146             var a;
5147             if(st){
5148                 a = Roo.DomQuery.getStyle(ci, attr);
5149             }
5150             else if(attr == "class" || attr == "className"){
5151                 a = ci.className;
5152             }else if(attr == "for"){
5153                 a = ci.htmlFor;
5154             }else if(attr == "href"){
5155                 a = ci.getAttribute("href", 2);
5156             }else{
5157                 a = ci.getAttribute(attr);
5158             }
5159             if((f && f(a, value)) || (!f && a)){
5160                 r[++ri] = ci;
5161             }
5162         }
5163         return r;
5164     };
5165
5166     function byPseudo(cs, name, value){
5167         return Roo.DomQuery.pseudos[name](cs, value);
5168     };
5169
5170     // This is for IE MSXML which does not support expandos.
5171     // IE runs the same speed using setAttribute, however FF slows way down
5172     // and Safari completely fails so they need to continue to use expandos.
5173     var isIE = window.ActiveXObject ? true : false;
5174
5175     // this eval is stop the compressor from
5176     // renaming the variable to something shorter
5177     
5178     /** eval:var:batch */
5179     var batch = 30803; 
5180
5181     var key = 30803;
5182
5183     function nodupIEXml(cs){
5184         var d = ++key;
5185         cs[0].setAttribute("_nodup", d);
5186         var r = [cs[0]];
5187         for(var i = 1, len = cs.length; i < len; i++){
5188             var c = cs[i];
5189             if(!c.getAttribute("_nodup") != d){
5190                 c.setAttribute("_nodup", d);
5191                 r[r.length] = c;
5192             }
5193         }
5194         for(var i = 0, len = cs.length; i < len; i++){
5195             cs[i].removeAttribute("_nodup");
5196         }
5197         return r;
5198     }
5199
5200     function nodup(cs){
5201         if(!cs){
5202             return [];
5203         }
5204         var len = cs.length, c, i, r = cs, cj, ri = -1;
5205         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5206             return cs;
5207         }
5208         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5209             return nodupIEXml(cs);
5210         }
5211         var d = ++key;
5212         cs[0]._nodup = d;
5213         for(i = 1; c = cs[i]; i++){
5214             if(c._nodup != d){
5215                 c._nodup = d;
5216             }else{
5217                 r = [];
5218                 for(var j = 0; j < i; j++){
5219                     r[++ri] = cs[j];
5220                 }
5221                 for(j = i+1; cj = cs[j]; j++){
5222                     if(cj._nodup != d){
5223                         cj._nodup = d;
5224                         r[++ri] = cj;
5225                     }
5226                 }
5227                 return r;
5228             }
5229         }
5230         return r;
5231     }
5232
5233     function quickDiffIEXml(c1, c2){
5234         var d = ++key;
5235         for(var i = 0, len = c1.length; i < len; i++){
5236             c1[i].setAttribute("_qdiff", d);
5237         }
5238         var r = [];
5239         for(var i = 0, len = c2.length; i < len; i++){
5240             if(c2[i].getAttribute("_qdiff") != d){
5241                 r[r.length] = c2[i];
5242             }
5243         }
5244         for(var i = 0, len = c1.length; i < len; i++){
5245            c1[i].removeAttribute("_qdiff");
5246         }
5247         return r;
5248     }
5249
5250     function quickDiff(c1, c2){
5251         var len1 = c1.length;
5252         if(!len1){
5253             return c2;
5254         }
5255         if(isIE && c1[0].selectSingleNode){
5256             return quickDiffIEXml(c1, c2);
5257         }
5258         var d = ++key;
5259         for(var i = 0; i < len1; i++){
5260             c1[i]._qdiff = d;
5261         }
5262         var r = [];
5263         for(var i = 0, len = c2.length; i < len; i++){
5264             if(c2[i]._qdiff != d){
5265                 r[r.length] = c2[i];
5266             }
5267         }
5268         return r;
5269     }
5270
5271     function quickId(ns, mode, root, id){
5272         if(ns == root){
5273            var d = root.ownerDocument || root;
5274            return d.getElementById(id);
5275         }
5276         ns = getNodes(ns, mode, "*");
5277         return byId(ns, null, id);
5278     }
5279
5280     return {
5281         getStyle : function(el, name){
5282             return Roo.fly(el).getStyle(name);
5283         },
5284         /**
5285          * Compiles a selector/xpath query into a reusable function. The returned function
5286          * takes one parameter "root" (optional), which is the context node from where the query should start.
5287          * @param {String} selector The selector/xpath query
5288          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5289          * @return {Function}
5290          */
5291         compile : function(path, type){
5292             type = type || "select";
5293             
5294             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5295             var q = path, mode, lq;
5296             var tk = Roo.DomQuery.matchers;
5297             var tklen = tk.length;
5298             var mm;
5299
5300             // accept leading mode switch
5301             var lmode = q.match(modeRe);
5302             if(lmode && lmode[1]){
5303                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5304                 q = q.replace(lmode[1], "");
5305             }
5306             // strip leading slashes
5307             while(path.substr(0, 1)=="/"){
5308                 path = path.substr(1);
5309             }
5310
5311             while(q && lq != q){
5312                 lq = q;
5313                 var tm = q.match(tagTokenRe);
5314                 if(type == "select"){
5315                     if(tm){
5316                         if(tm[1] == "#"){
5317                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5318                         }else{
5319                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5320                         }
5321                         q = q.replace(tm[0], "");
5322                     }else if(q.substr(0, 1) != '@'){
5323                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5324                     }
5325                 }else{
5326                     if(tm){
5327                         if(tm[1] == "#"){
5328                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5329                         }else{
5330                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5331                         }
5332                         q = q.replace(tm[0], "");
5333                     }
5334                 }
5335                 while(!(mm = q.match(modeRe))){
5336                     var matched = false;
5337                     for(var j = 0; j < tklen; j++){
5338                         var t = tk[j];
5339                         var m = q.match(t.re);
5340                         if(m){
5341                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5342                                                     return m[i];
5343                                                 });
5344                             q = q.replace(m[0], "");
5345                             matched = true;
5346                             break;
5347                         }
5348                     }
5349                     // prevent infinite loop on bad selector
5350                     if(!matched){
5351                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5352                     }
5353                 }
5354                 if(mm[1]){
5355                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5356                     q = q.replace(mm[1], "");
5357                 }
5358             }
5359             fn[fn.length] = "return nodup(n);\n}";
5360             
5361              /** 
5362               * list of variables that need from compression as they are used by eval.
5363              *  eval:var:batch 
5364              *  eval:var:nodup
5365              *  eval:var:byTag
5366              *  eval:var:ById
5367              *  eval:var:getNodes
5368              *  eval:var:quickId
5369              *  eval:var:mode
5370              *  eval:var:root
5371              *  eval:var:n
5372              *  eval:var:byClassName
5373              *  eval:var:byPseudo
5374              *  eval:var:byAttribute
5375              *  eval:var:attrValue
5376              * 
5377              **/ 
5378             eval(fn.join(""));
5379             return f;
5380         },
5381
5382         /**
5383          * Selects a group of elements.
5384          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5385          * @param {Node} root (optional) The start of the query (defaults to document).
5386          * @return {Array}
5387          */
5388         select : function(path, root, type){
5389             if(!root || root == document){
5390                 root = document;
5391             }
5392             if(typeof root == "string"){
5393                 root = document.getElementById(root);
5394             }
5395             var paths = path.split(",");
5396             var results = [];
5397             for(var i = 0, len = paths.length; i < len; i++){
5398                 var p = paths[i].replace(trimRe, "");
5399                 if(!cache[p]){
5400                     cache[p] = Roo.DomQuery.compile(p);
5401                     if(!cache[p]){
5402                         throw p + " is not a valid selector";
5403                     }
5404                 }
5405                 var result = cache[p](root);
5406                 if(result && result != document){
5407                     results = results.concat(result);
5408                 }
5409             }
5410             if(paths.length > 1){
5411                 return nodup(results);
5412             }
5413             return results;
5414         },
5415
5416         /**
5417          * Selects a single element.
5418          * @param {String} selector The selector/xpath query
5419          * @param {Node} root (optional) The start of the query (defaults to document).
5420          * @return {Element}
5421          */
5422         selectNode : function(path, root){
5423             return Roo.DomQuery.select(path, root)[0];
5424         },
5425
5426         /**
5427          * Selects the value of a node, optionally replacing null with the defaultValue.
5428          * @param {String} selector The selector/xpath query
5429          * @param {Node} root (optional) The start of the query (defaults to document).
5430          * @param {String} defaultValue
5431          */
5432         selectValue : function(path, root, defaultValue){
5433             path = path.replace(trimRe, "");
5434             if(!valueCache[path]){
5435                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5436             }
5437             var n = valueCache[path](root);
5438             n = n[0] ? n[0] : n;
5439             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5440             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5441         },
5442
5443         /**
5444          * Selects the value of a node, parsing integers and floats.
5445          * @param {String} selector The selector/xpath query
5446          * @param {Node} root (optional) The start of the query (defaults to document).
5447          * @param {Number} defaultValue
5448          * @return {Number}
5449          */
5450         selectNumber : function(path, root, defaultValue){
5451             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5452             return parseFloat(v);
5453         },
5454
5455         /**
5456          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5457          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5458          * @param {String} selector The simple selector to test
5459          * @return {Boolean}
5460          */
5461         is : function(el, ss){
5462             if(typeof el == "string"){
5463                 el = document.getElementById(el);
5464             }
5465             var isArray = (el instanceof Array);
5466             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5467             return isArray ? (result.length == el.length) : (result.length > 0);
5468         },
5469
5470         /**
5471          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5472          * @param {Array} el An array of elements to filter
5473          * @param {String} selector The simple selector to test
5474          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5475          * the selector instead of the ones that match
5476          * @return {Array}
5477          */
5478         filter : function(els, ss, nonMatches){
5479             ss = ss.replace(trimRe, "");
5480             if(!simpleCache[ss]){
5481                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5482             }
5483             var result = simpleCache[ss](els);
5484             return nonMatches ? quickDiff(result, els) : result;
5485         },
5486
5487         /**
5488          * Collection of matching regular expressions and code snippets.
5489          */
5490         matchers : [{
5491                 re: /^\.([\w-]+)/,
5492                 select: 'n = byClassName(n, null, " {1} ");'
5493             }, {
5494                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5495                 select: 'n = byPseudo(n, "{1}", "{2}");'
5496             },{
5497                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5498                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5499             }, {
5500                 re: /^#([\w-]+)/,
5501                 select: 'n = byId(n, null, "{1}");'
5502             },{
5503                 re: /^@([\w-]+)/,
5504                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5505             }
5506         ],
5507
5508         /**
5509          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5510          * 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;.
5511          */
5512         operators : {
5513             "=" : function(a, v){
5514                 return a == v;
5515             },
5516             "!=" : function(a, v){
5517                 return a != v;
5518             },
5519             "^=" : function(a, v){
5520                 return a && a.substr(0, v.length) == v;
5521             },
5522             "$=" : function(a, v){
5523                 return a && a.substr(a.length-v.length) == v;
5524             },
5525             "*=" : function(a, v){
5526                 return a && a.indexOf(v) !== -1;
5527             },
5528             "%=" : function(a, v){
5529                 return (a % v) == 0;
5530             },
5531             "|=" : function(a, v){
5532                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5533             },
5534             "~=" : function(a, v){
5535                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5536             }
5537         },
5538
5539         /**
5540          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5541          * and the argument (if any) supplied in the selector.
5542          */
5543         pseudos : {
5544             "first-child" : function(c){
5545                 var r = [], ri = -1, n;
5546                 for(var i = 0, ci; ci = n = c[i]; i++){
5547                     while((n = n.previousSibling) && n.nodeType != 1);
5548                     if(!n){
5549                         r[++ri] = ci;
5550                     }
5551                 }
5552                 return r;
5553             },
5554
5555             "last-child" : function(c){
5556                 var r = [], ri = -1, n;
5557                 for(var i = 0, ci; ci = n = c[i]; i++){
5558                     while((n = n.nextSibling) && n.nodeType != 1);
5559                     if(!n){
5560                         r[++ri] = ci;
5561                     }
5562                 }
5563                 return r;
5564             },
5565
5566             "nth-child" : function(c, a) {
5567                 var r = [], ri = -1;
5568                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5569                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5570                 for(var i = 0, n; n = c[i]; i++){
5571                     var pn = n.parentNode;
5572                     if (batch != pn._batch) {
5573                         var j = 0;
5574                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5575                             if(cn.nodeType == 1){
5576                                cn.nodeIndex = ++j;
5577                             }
5578                         }
5579                         pn._batch = batch;
5580                     }
5581                     if (f == 1) {
5582                         if (l == 0 || n.nodeIndex == l){
5583                             r[++ri] = n;
5584                         }
5585                     } else if ((n.nodeIndex + l) % f == 0){
5586                         r[++ri] = n;
5587                     }
5588                 }
5589
5590                 return r;
5591             },
5592
5593             "only-child" : function(c){
5594                 var r = [], ri = -1;;
5595                 for(var i = 0, ci; ci = c[i]; i++){
5596                     if(!prev(ci) && !next(ci)){
5597                         r[++ri] = ci;
5598                     }
5599                 }
5600                 return r;
5601             },
5602
5603             "empty" : function(c){
5604                 var r = [], ri = -1;
5605                 for(var i = 0, ci; ci = c[i]; i++){
5606                     var cns = ci.childNodes, j = 0, cn, empty = true;
5607                     while(cn = cns[j]){
5608                         ++j;
5609                         if(cn.nodeType == 1 || cn.nodeType == 3){
5610                             empty = false;
5611                             break;
5612                         }
5613                     }
5614                     if(empty){
5615                         r[++ri] = ci;
5616                     }
5617                 }
5618                 return r;
5619             },
5620
5621             "contains" : function(c, v){
5622                 var r = [], ri = -1;
5623                 for(var i = 0, ci; ci = c[i]; i++){
5624                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5625                         r[++ri] = ci;
5626                     }
5627                 }
5628                 return r;
5629             },
5630
5631             "nodeValue" : function(c, v){
5632                 var r = [], ri = -1;
5633                 for(var i = 0, ci; ci = c[i]; i++){
5634                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5635                         r[++ri] = ci;
5636                     }
5637                 }
5638                 return r;
5639             },
5640
5641             "checked" : function(c){
5642                 var r = [], ri = -1;
5643                 for(var i = 0, ci; ci = c[i]; i++){
5644                     if(ci.checked == true){
5645                         r[++ri] = ci;
5646                     }
5647                 }
5648                 return r;
5649             },
5650
5651             "not" : function(c, ss){
5652                 return Roo.DomQuery.filter(c, ss, true);
5653             },
5654
5655             "odd" : function(c){
5656                 return this["nth-child"](c, "odd");
5657             },
5658
5659             "even" : function(c){
5660                 return this["nth-child"](c, "even");
5661             },
5662
5663             "nth" : function(c, a){
5664                 return c[a-1] || [];
5665             },
5666
5667             "first" : function(c){
5668                 return c[0] || [];
5669             },
5670
5671             "last" : function(c){
5672                 return c[c.length-1] || [];
5673             },
5674
5675             "has" : function(c, ss){
5676                 var s = Roo.DomQuery.select;
5677                 var r = [], ri = -1;
5678                 for(var i = 0, ci; ci = c[i]; i++){
5679                     if(s(ss, ci).length > 0){
5680                         r[++ri] = ci;
5681                     }
5682                 }
5683                 return r;
5684             },
5685
5686             "next" : function(c, ss){
5687                 var is = Roo.DomQuery.is;
5688                 var r = [], ri = -1;
5689                 for(var i = 0, ci; ci = c[i]; i++){
5690                     var n = next(ci);
5691                     if(n && is(n, ss)){
5692                         r[++ri] = ci;
5693                     }
5694                 }
5695                 return r;
5696             },
5697
5698             "prev" : function(c, ss){
5699                 var is = Roo.DomQuery.is;
5700                 var r = [], ri = -1;
5701                 for(var i = 0, ci; ci = c[i]; i++){
5702                     var n = prev(ci);
5703                     if(n && is(n, ss)){
5704                         r[++ri] = ci;
5705                     }
5706                 }
5707                 return r;
5708             }
5709         }
5710     };
5711 }();
5712
5713 /**
5714  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5715  * @param {String} path The selector/xpath query
5716  * @param {Node} root (optional) The start of the query (defaults to document).
5717  * @return {Array}
5718  * @member Roo
5719  * @method query
5720  */
5721 Roo.query = Roo.DomQuery.select;
5722 /*
5723  * Based on:
5724  * Ext JS Library 1.1.1
5725  * Copyright(c) 2006-2007, Ext JS, LLC.
5726  *
5727  * Originally Released Under LGPL - original licence link has changed is not relivant.
5728  *
5729  * Fork - LGPL
5730  * <script type="text/javascript">
5731  */
5732
5733 /**
5734  * @class Roo.util.Observable
5735  * Base class that provides a common interface for publishing events. Subclasses are expected to
5736  * to have a property "events" with all the events defined.<br>
5737  * For example:
5738  * <pre><code>
5739  Employee = function(name){
5740     this.name = name;
5741     this.addEvents({
5742         "fired" : true,
5743         "quit" : true
5744     });
5745  }
5746  Roo.extend(Employee, Roo.util.Observable);
5747 </code></pre>
5748  * @param {Object} config properties to use (incuding events / listeners)
5749  */
5750
5751 Roo.util.Observable = function(cfg){
5752     
5753     cfg = cfg|| {};
5754     this.addEvents(cfg.events || {});
5755     if (cfg.events) {
5756         delete cfg.events; // make sure
5757     }
5758      
5759     Roo.apply(this, cfg);
5760     
5761     if(this.listeners){
5762         this.on(this.listeners);
5763         delete this.listeners;
5764     }
5765 };
5766 Roo.util.Observable.prototype = {
5767     /** 
5768  * @cfg {Object} listeners  list of events and functions to call for this object, 
5769  * For example :
5770  * <pre><code>
5771     listeners :  { 
5772        'click' : function(e) {
5773            ..... 
5774         } ,
5775         .... 
5776     } 
5777   </code></pre>
5778  */
5779     
5780     
5781     /**
5782      * Fires the specified event with the passed parameters (minus the event name).
5783      * @param {String} eventName
5784      * @param {Object...} args Variable number of parameters are passed to handlers
5785      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5786      */
5787     fireEvent : function(){
5788         var ce = this.events[arguments[0].toLowerCase()];
5789         if(typeof ce == "object"){
5790             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5791         }else{
5792             return true;
5793         }
5794     },
5795
5796     // private
5797     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5798
5799     /**
5800      * Appends an event handler to this component
5801      * @param {String}   eventName The type of event to listen for
5802      * @param {Function} handler The method the event invokes
5803      * @param {Object}   scope (optional) The scope in which to execute the handler
5804      * function. The handler function's "this" context.
5805      * @param {Object}   options (optional) An object containing handler configuration
5806      * properties. This may contain any of the following properties:<ul>
5807      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5808      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5809      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5810      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5811      * by the specified number of milliseconds. If the event fires again within that time, the original
5812      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5813      * </ul><br>
5814      * <p>
5815      * <b>Combining Options</b><br>
5816      * Using the options argument, it is possible to combine different types of listeners:<br>
5817      * <br>
5818      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5819                 <pre><code>
5820                 el.on('click', this.onClick, this, {
5821                         single: true,
5822                 delay: 100,
5823                 forumId: 4
5824                 });
5825                 </code></pre>
5826      * <p>
5827      * <b>Attaching multiple handlers in 1 call</b><br>
5828      * The method also allows for a single argument to be passed which is a config object containing properties
5829      * which specify multiple handlers.
5830      * <pre><code>
5831                 el.on({
5832                         'click': {
5833                         fn: this.onClick,
5834                         scope: this,
5835                         delay: 100
5836                 }, 
5837                 'mouseover': {
5838                         fn: this.onMouseOver,
5839                         scope: this
5840                 },
5841                 'mouseout': {
5842                         fn: this.onMouseOut,
5843                         scope: this
5844                 }
5845                 });
5846                 </code></pre>
5847      * <p>
5848      * Or a shorthand syntax which passes the same scope object to all handlers:
5849         <pre><code>
5850                 el.on({
5851                         'click': this.onClick,
5852                 'mouseover': this.onMouseOver,
5853                 'mouseout': this.onMouseOut,
5854                 scope: this
5855                 });
5856                 </code></pre>
5857      */
5858     addListener : function(eventName, fn, scope, o){
5859         if(typeof eventName == "object"){
5860             o = eventName;
5861             for(var e in o){
5862                 if(this.filterOptRe.test(e)){
5863                     continue;
5864                 }
5865                 if(typeof o[e] == "function"){
5866                     // shared options
5867                     this.addListener(e, o[e], o.scope,  o);
5868                 }else{
5869                     // individual options
5870                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5871                 }
5872             }
5873             return;
5874         }
5875         o = (!o || typeof o == "boolean") ? {} : o;
5876         eventName = eventName.toLowerCase();
5877         var ce = this.events[eventName] || true;
5878         if(typeof ce == "boolean"){
5879             ce = new Roo.util.Event(this, eventName);
5880             this.events[eventName] = ce;
5881         }
5882         ce.addListener(fn, scope, o);
5883     },
5884
5885     /**
5886      * Removes a listener
5887      * @param {String}   eventName     The type of event to listen for
5888      * @param {Function} handler        The handler to remove
5889      * @param {Object}   scope  (optional) The scope (this object) for the handler
5890      */
5891     removeListener : function(eventName, fn, scope){
5892         var ce = this.events[eventName.toLowerCase()];
5893         if(typeof ce == "object"){
5894             ce.removeListener(fn, scope);
5895         }
5896     },
5897
5898     /**
5899      * Removes all listeners for this object
5900      */
5901     purgeListeners : function(){
5902         for(var evt in this.events){
5903             if(typeof this.events[evt] == "object"){
5904                  this.events[evt].clearListeners();
5905             }
5906         }
5907     },
5908
5909     relayEvents : function(o, events){
5910         var createHandler = function(ename){
5911             return function(){
5912                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5913             };
5914         };
5915         for(var i = 0, len = events.length; i < len; i++){
5916             var ename = events[i];
5917             if(!this.events[ename]){ this.events[ename] = true; };
5918             o.on(ename, createHandler(ename), this);
5919         }
5920     },
5921
5922     /**
5923      * Used to define events on this Observable
5924      * @param {Object} object The object with the events defined
5925      */
5926     addEvents : function(o){
5927         if(!this.events){
5928             this.events = {};
5929         }
5930         Roo.applyIf(this.events, o);
5931     },
5932
5933     /**
5934      * Checks to see if this object has any listeners for a specified event
5935      * @param {String} eventName The name of the event to check for
5936      * @return {Boolean} True if the event is being listened for, else false
5937      */
5938     hasListener : function(eventName){
5939         var e = this.events[eventName];
5940         return typeof e == "object" && e.listeners.length > 0;
5941     }
5942 };
5943 /**
5944  * Appends an event handler to this element (shorthand for addListener)
5945  * @param {String}   eventName     The type of event to listen for
5946  * @param {Function} handler        The method the event invokes
5947  * @param {Object}   scope (optional) The scope in which to execute the handler
5948  * function. The handler function's "this" context.
5949  * @param {Object}   options  (optional)
5950  * @method
5951  */
5952 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5953 /**
5954  * Removes a listener (shorthand for removeListener)
5955  * @param {String}   eventName     The type of event to listen for
5956  * @param {Function} handler        The handler to remove
5957  * @param {Object}   scope  (optional) The scope (this object) for the handler
5958  * @method
5959  */
5960 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5961
5962 /**
5963  * Starts capture on the specified Observable. All events will be passed
5964  * to the supplied function with the event name + standard signature of the event
5965  * <b>before</b> the event is fired. If the supplied function returns false,
5966  * the event will not fire.
5967  * @param {Observable} o The Observable to capture
5968  * @param {Function} fn The function to call
5969  * @param {Object} scope (optional) The scope (this object) for the fn
5970  * @static
5971  */
5972 Roo.util.Observable.capture = function(o, fn, scope){
5973     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5974 };
5975
5976 /**
5977  * Removes <b>all</b> added captures from the Observable.
5978  * @param {Observable} o The Observable to release
5979  * @static
5980  */
5981 Roo.util.Observable.releaseCapture = function(o){
5982     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5983 };
5984
5985 (function(){
5986
5987     var createBuffered = function(h, o, scope){
5988         var task = new Roo.util.DelayedTask();
5989         return function(){
5990             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5991         };
5992     };
5993
5994     var createSingle = function(h, e, fn, scope){
5995         return function(){
5996             e.removeListener(fn, scope);
5997             return h.apply(scope, arguments);
5998         };
5999     };
6000
6001     var createDelayed = function(h, o, scope){
6002         return function(){
6003             var args = Array.prototype.slice.call(arguments, 0);
6004             setTimeout(function(){
6005                 h.apply(scope, args);
6006             }, o.delay || 10);
6007         };
6008     };
6009
6010     Roo.util.Event = function(obj, name){
6011         this.name = name;
6012         this.obj = obj;
6013         this.listeners = [];
6014     };
6015
6016     Roo.util.Event.prototype = {
6017         addListener : function(fn, scope, options){
6018             var o = options || {};
6019             scope = scope || this.obj;
6020             if(!this.isListening(fn, scope)){
6021                 var l = {fn: fn, scope: scope, options: o};
6022                 var h = fn;
6023                 if(o.delay){
6024                     h = createDelayed(h, o, scope);
6025                 }
6026                 if(o.single){
6027                     h = createSingle(h, this, fn, scope);
6028                 }
6029                 if(o.buffer){
6030                     h = createBuffered(h, o, scope);
6031                 }
6032                 l.fireFn = h;
6033                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6034                     this.listeners.push(l);
6035                 }else{
6036                     this.listeners = this.listeners.slice(0);
6037                     this.listeners.push(l);
6038                 }
6039             }
6040         },
6041
6042         findListener : function(fn, scope){
6043             scope = scope || this.obj;
6044             var ls = this.listeners;
6045             for(var i = 0, len = ls.length; i < len; i++){
6046                 var l = ls[i];
6047                 if(l.fn == fn && l.scope == scope){
6048                     return i;
6049                 }
6050             }
6051             return -1;
6052         },
6053
6054         isListening : function(fn, scope){
6055             return this.findListener(fn, scope) != -1;
6056         },
6057
6058         removeListener : function(fn, scope){
6059             var index;
6060             if((index = this.findListener(fn, scope)) != -1){
6061                 if(!this.firing){
6062                     this.listeners.splice(index, 1);
6063                 }else{
6064                     this.listeners = this.listeners.slice(0);
6065                     this.listeners.splice(index, 1);
6066                 }
6067                 return true;
6068             }
6069             return false;
6070         },
6071
6072         clearListeners : function(){
6073             this.listeners = [];
6074         },
6075
6076         fire : function(){
6077             var ls = this.listeners, scope, len = ls.length;
6078             if(len > 0){
6079                 this.firing = true;
6080                 var args = Array.prototype.slice.call(arguments, 0);
6081                 for(var i = 0; i < len; i++){
6082                     var l = ls[i];
6083                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6084                         this.firing = false;
6085                         return false;
6086                     }
6087                 }
6088                 this.firing = false;
6089             }
6090             return true;
6091         }
6092     };
6093 })();/*
6094  * RooJS Library 
6095  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6096  *
6097  * Licence LGPL 
6098  *
6099  */
6100  
6101 /**
6102  * @class Roo.Document
6103  * @extends Roo.util.Observable
6104  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6105  * 
6106  * @param {Object} config the methods and properties of the 'base' class for the application.
6107  * 
6108  *  Generic Page handler - implement this to start your app..
6109  * 
6110  * eg.
6111  *  MyProject = new Roo.Document({
6112         events : {
6113             'load' : true // your events..
6114         },
6115         listeners : {
6116             'ready' : function() {
6117                 // fired on Roo.onReady()
6118             }
6119         }
6120  * 
6121  */
6122 Roo.Document = function(cfg) {
6123      
6124     this.addEvents({ 
6125         'ready' : true
6126     });
6127     Roo.util.Observable.call(this,cfg);
6128     
6129     var _this = this;
6130     
6131     Roo.onReady(function() {
6132         _this.fireEvent('ready');
6133     },null,false);
6134     
6135     
6136 }
6137
6138 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6139  * Based on:
6140  * Ext JS Library 1.1.1
6141  * Copyright(c) 2006-2007, Ext JS, LLC.
6142  *
6143  * Originally Released Under LGPL - original licence link has changed is not relivant.
6144  *
6145  * Fork - LGPL
6146  * <script type="text/javascript">
6147  */
6148
6149 /**
6150  * @class Roo.EventManager
6151  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6152  * several useful events directly.
6153  * See {@link Roo.EventObject} for more details on normalized event objects.
6154  * @singleton
6155  */
6156 Roo.EventManager = function(){
6157     var docReadyEvent, docReadyProcId, docReadyState = false;
6158     var resizeEvent, resizeTask, textEvent, textSize;
6159     var E = Roo.lib.Event;
6160     var D = Roo.lib.Dom;
6161
6162     
6163     
6164
6165     var fireDocReady = function(){
6166         if(!docReadyState){
6167             docReadyState = true;
6168             Roo.isReady = true;
6169             if(docReadyProcId){
6170                 clearInterval(docReadyProcId);
6171             }
6172             if(Roo.isGecko || Roo.isOpera) {
6173                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6174             }
6175             if(Roo.isIE){
6176                 var defer = document.getElementById("ie-deferred-loader");
6177                 if(defer){
6178                     defer.onreadystatechange = null;
6179                     defer.parentNode.removeChild(defer);
6180                 }
6181             }
6182             if(docReadyEvent){
6183                 docReadyEvent.fire();
6184                 docReadyEvent.clearListeners();
6185             }
6186         }
6187     };
6188     
6189     var initDocReady = function(){
6190         docReadyEvent = new Roo.util.Event();
6191         if(Roo.isGecko || Roo.isOpera) {
6192             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6193         }else if(Roo.isIE){
6194             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6195             var defer = document.getElementById("ie-deferred-loader");
6196             defer.onreadystatechange = function(){
6197                 if(this.readyState == "complete"){
6198                     fireDocReady();
6199                 }
6200             };
6201         }else if(Roo.isSafari){ 
6202             docReadyProcId = setInterval(function(){
6203                 var rs = document.readyState;
6204                 if(rs == "complete") {
6205                     fireDocReady();     
6206                  }
6207             }, 10);
6208         }
6209         // no matter what, make sure it fires on load
6210         E.on(window, "load", fireDocReady);
6211     };
6212
6213     var createBuffered = function(h, o){
6214         var task = new Roo.util.DelayedTask(h);
6215         return function(e){
6216             // create new event object impl so new events don't wipe out properties
6217             e = new Roo.EventObjectImpl(e);
6218             task.delay(o.buffer, h, null, [e]);
6219         };
6220     };
6221
6222     var createSingle = function(h, el, ename, fn){
6223         return function(e){
6224             Roo.EventManager.removeListener(el, ename, fn);
6225             h(e);
6226         };
6227     };
6228
6229     var createDelayed = function(h, o){
6230         return function(e){
6231             // create new event object impl so new events don't wipe out properties
6232             e = new Roo.EventObjectImpl(e);
6233             setTimeout(function(){
6234                 h(e);
6235             }, o.delay || 10);
6236         };
6237     };
6238     var transitionEndVal = false;
6239     
6240     var transitionEnd = function()
6241     {
6242         if (transitionEndVal) {
6243             return transitionEndVal;
6244         }
6245         var el = document.createElement('div');
6246
6247         var transEndEventNames = {
6248             WebkitTransition : 'webkitTransitionEnd',
6249             MozTransition    : 'transitionend',
6250             OTransition      : 'oTransitionEnd otransitionend',
6251             transition       : 'transitionend'
6252         };
6253     
6254         for (var name in transEndEventNames) {
6255             if (el.style[name] !== undefined) {
6256                 transitionEndVal = transEndEventNames[name];
6257                 return  transitionEndVal ;
6258             }
6259         }
6260     }
6261     
6262
6263     var listen = function(element, ename, opt, fn, scope){
6264         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6265         fn = fn || o.fn; scope = scope || o.scope;
6266         var el = Roo.getDom(element);
6267         
6268         
6269         if(!el){
6270             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6271         }
6272         
6273         if (ename == 'transitionend') {
6274             ename = transitionEnd();
6275         }
6276         var h = function(e){
6277             e = Roo.EventObject.setEvent(e);
6278             var t;
6279             if(o.delegate){
6280                 t = e.getTarget(o.delegate, el);
6281                 if(!t){
6282                     return;
6283                 }
6284             }else{
6285                 t = e.target;
6286             }
6287             if(o.stopEvent === true){
6288                 e.stopEvent();
6289             }
6290             if(o.preventDefault === true){
6291                e.preventDefault();
6292             }
6293             if(o.stopPropagation === true){
6294                 e.stopPropagation();
6295             }
6296
6297             if(o.normalized === false){
6298                 e = e.browserEvent;
6299             }
6300
6301             fn.call(scope || el, e, t, o);
6302         };
6303         if(o.delay){
6304             h = createDelayed(h, o);
6305         }
6306         if(o.single){
6307             h = createSingle(h, el, ename, fn);
6308         }
6309         if(o.buffer){
6310             h = createBuffered(h, o);
6311         }
6312         
6313         fn._handlers = fn._handlers || [];
6314         
6315         
6316         fn._handlers.push([Roo.id(el), ename, h]);
6317         
6318         
6319          
6320         E.on(el, ename, h);
6321         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6322             el.addEventListener("DOMMouseScroll", h, false);
6323             E.on(window, 'unload', function(){
6324                 el.removeEventListener("DOMMouseScroll", h, false);
6325             });
6326         }
6327         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6328             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6329         }
6330         return h;
6331     };
6332
6333     var stopListening = function(el, ename, fn){
6334         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6335         if(hds){
6336             for(var i = 0, len = hds.length; i < len; i++){
6337                 var h = hds[i];
6338                 if(h[0] == id && h[1] == ename){
6339                     hd = h[2];
6340                     hds.splice(i, 1);
6341                     break;
6342                 }
6343             }
6344         }
6345         E.un(el, ename, hd);
6346         el = Roo.getDom(el);
6347         if(ename == "mousewheel" && el.addEventListener){
6348             el.removeEventListener("DOMMouseScroll", hd, false);
6349         }
6350         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6351             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6352         }
6353     };
6354
6355     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6356     
6357     var pub = {
6358         
6359         
6360         /** 
6361          * Fix for doc tools
6362          * @scope Roo.EventManager
6363          */
6364         
6365         
6366         /** 
6367          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6368          * object with a Roo.EventObject
6369          * @param {Function} fn        The method the event invokes
6370          * @param {Object}   scope    An object that becomes the scope of the handler
6371          * @param {boolean}  override If true, the obj passed in becomes
6372          *                             the execution scope of the listener
6373          * @return {Function} The wrapped function
6374          * @deprecated
6375          */
6376         wrap : function(fn, scope, override){
6377             return function(e){
6378                 Roo.EventObject.setEvent(e);
6379                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6380             };
6381         },
6382         
6383         /**
6384      * Appends an event handler to an element (shorthand for addListener)
6385      * @param {String/HTMLElement}   element        The html element or id to assign the
6386      * @param {String}   eventName The type of event to listen for
6387      * @param {Function} handler The method the event invokes
6388      * @param {Object}   scope (optional) The scope in which to execute the handler
6389      * function. The handler function's "this" context.
6390      * @param {Object}   options (optional) An object containing handler configuration
6391      * properties. This may contain any of the following properties:<ul>
6392      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6393      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6394      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6395      * <li>preventDefault {Boolean} True to prevent the default action</li>
6396      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6397      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6398      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6399      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6400      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6401      * by the specified number of milliseconds. If the event fires again within that time, the original
6402      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6403      * </ul><br>
6404      * <p>
6405      * <b>Combining Options</b><br>
6406      * Using the options argument, it is possible to combine different types of listeners:<br>
6407      * <br>
6408      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6409      * Code:<pre><code>
6410 el.on('click', this.onClick, this, {
6411     single: true,
6412     delay: 100,
6413     stopEvent : true,
6414     forumId: 4
6415 });</code></pre>
6416      * <p>
6417      * <b>Attaching multiple handlers in 1 call</b><br>
6418       * The method also allows for a single argument to be passed which is a config object containing properties
6419      * which specify multiple handlers.
6420      * <p>
6421      * Code:<pre><code>
6422 el.on({
6423     'click' : {
6424         fn: this.onClick
6425         scope: this,
6426         delay: 100
6427     },
6428     'mouseover' : {
6429         fn: this.onMouseOver
6430         scope: this
6431     },
6432     'mouseout' : {
6433         fn: this.onMouseOut
6434         scope: this
6435     }
6436 });</code></pre>
6437      * <p>
6438      * Or a shorthand syntax:<br>
6439      * Code:<pre><code>
6440 el.on({
6441     'click' : this.onClick,
6442     'mouseover' : this.onMouseOver,
6443     'mouseout' : this.onMouseOut
6444     scope: this
6445 });</code></pre>
6446      */
6447         addListener : function(element, eventName, fn, scope, options){
6448             if(typeof eventName == "object"){
6449                 var o = eventName;
6450                 for(var e in o){
6451                     if(propRe.test(e)){
6452                         continue;
6453                     }
6454                     if(typeof o[e] == "function"){
6455                         // shared options
6456                         listen(element, e, o, o[e], o.scope);
6457                     }else{
6458                         // individual options
6459                         listen(element, e, o[e]);
6460                     }
6461                 }
6462                 return;
6463             }
6464             return listen(element, eventName, options, fn, scope);
6465         },
6466         
6467         /**
6468          * Removes an event handler
6469          *
6470          * @param {String/HTMLElement}   element        The id or html element to remove the 
6471          *                             event from
6472          * @param {String}   eventName     The type of event
6473          * @param {Function} fn
6474          * @return {Boolean} True if a listener was actually removed
6475          */
6476         removeListener : function(element, eventName, fn){
6477             return stopListening(element, eventName, fn);
6478         },
6479         
6480         /**
6481          * Fires when the document is ready (before onload and before images are loaded). Can be 
6482          * accessed shorthanded Roo.onReady().
6483          * @param {Function} fn        The method the event invokes
6484          * @param {Object}   scope    An  object that becomes the scope of the handler
6485          * @param {boolean}  options
6486          */
6487         onDocumentReady : function(fn, scope, options){
6488             if(docReadyState){ // if it already fired
6489                 docReadyEvent.addListener(fn, scope, options);
6490                 docReadyEvent.fire();
6491                 docReadyEvent.clearListeners();
6492                 return;
6493             }
6494             if(!docReadyEvent){
6495                 initDocReady();
6496             }
6497             docReadyEvent.addListener(fn, scope, options);
6498         },
6499         
6500         /**
6501          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6502          * @param {Function} fn        The method the event invokes
6503          * @param {Object}   scope    An object that becomes the scope of the handler
6504          * @param {boolean}  options
6505          */
6506         onWindowResize : function(fn, scope, options){
6507             if(!resizeEvent){
6508                 resizeEvent = new Roo.util.Event();
6509                 resizeTask = new Roo.util.DelayedTask(function(){
6510                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6511                 });
6512                 E.on(window, "resize", function(){
6513                     if(Roo.isIE){
6514                         resizeTask.delay(50);
6515                     }else{
6516                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6517                     }
6518                 });
6519             }
6520             resizeEvent.addListener(fn, scope, options);
6521         },
6522
6523         /**
6524          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6525          * @param {Function} fn        The method the event invokes
6526          * @param {Object}   scope    An object that becomes the scope of the handler
6527          * @param {boolean}  options
6528          */
6529         onTextResize : function(fn, scope, options){
6530             if(!textEvent){
6531                 textEvent = new Roo.util.Event();
6532                 var textEl = new Roo.Element(document.createElement('div'));
6533                 textEl.dom.className = 'x-text-resize';
6534                 textEl.dom.innerHTML = 'X';
6535                 textEl.appendTo(document.body);
6536                 textSize = textEl.dom.offsetHeight;
6537                 setInterval(function(){
6538                     if(textEl.dom.offsetHeight != textSize){
6539                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6540                     }
6541                 }, this.textResizeInterval);
6542             }
6543             textEvent.addListener(fn, scope, options);
6544         },
6545
6546         /**
6547          * Removes the passed window resize listener.
6548          * @param {Function} fn        The method the event invokes
6549          * @param {Object}   scope    The scope of handler
6550          */
6551         removeResizeListener : function(fn, scope){
6552             if(resizeEvent){
6553                 resizeEvent.removeListener(fn, scope);
6554             }
6555         },
6556
6557         // private
6558         fireResize : function(){
6559             if(resizeEvent){
6560                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6561             }   
6562         },
6563         /**
6564          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6565          */
6566         ieDeferSrc : false,
6567         /**
6568          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6569          */
6570         textResizeInterval : 50
6571     };
6572     
6573     /**
6574      * Fix for doc tools
6575      * @scopeAlias pub=Roo.EventManager
6576      */
6577     
6578      /**
6579      * Appends an event handler to an element (shorthand for addListener)
6580      * @param {String/HTMLElement}   element        The html element or id to assign the
6581      * @param {String}   eventName The type of event to listen for
6582      * @param {Function} handler The method the event invokes
6583      * @param {Object}   scope (optional) The scope in which to execute the handler
6584      * function. The handler function's "this" context.
6585      * @param {Object}   options (optional) An object containing handler configuration
6586      * properties. This may contain any of the following properties:<ul>
6587      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6588      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6589      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6590      * <li>preventDefault {Boolean} True to prevent the default action</li>
6591      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6592      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6593      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6594      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6595      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6596      * by the specified number of milliseconds. If the event fires again within that time, the original
6597      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6598      * </ul><br>
6599      * <p>
6600      * <b>Combining Options</b><br>
6601      * Using the options argument, it is possible to combine different types of listeners:<br>
6602      * <br>
6603      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6604      * Code:<pre><code>
6605 el.on('click', this.onClick, this, {
6606     single: true,
6607     delay: 100,
6608     stopEvent : true,
6609     forumId: 4
6610 });</code></pre>
6611      * <p>
6612      * <b>Attaching multiple handlers in 1 call</b><br>
6613       * The method also allows for a single argument to be passed which is a config object containing properties
6614      * which specify multiple handlers.
6615      * <p>
6616      * Code:<pre><code>
6617 el.on({
6618     'click' : {
6619         fn: this.onClick
6620         scope: this,
6621         delay: 100
6622     },
6623     'mouseover' : {
6624         fn: this.onMouseOver
6625         scope: this
6626     },
6627     'mouseout' : {
6628         fn: this.onMouseOut
6629         scope: this
6630     }
6631 });</code></pre>
6632      * <p>
6633      * Or a shorthand syntax:<br>
6634      * Code:<pre><code>
6635 el.on({
6636     'click' : this.onClick,
6637     'mouseover' : this.onMouseOver,
6638     'mouseout' : this.onMouseOut
6639     scope: this
6640 });</code></pre>
6641      */
6642     pub.on = pub.addListener;
6643     pub.un = pub.removeListener;
6644
6645     pub.stoppedMouseDownEvent = new Roo.util.Event();
6646     return pub;
6647 }();
6648 /**
6649   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6650   * @param {Function} fn        The method the event invokes
6651   * @param {Object}   scope    An  object that becomes the scope of the handler
6652   * @param {boolean}  override If true, the obj passed in becomes
6653   *                             the execution scope of the listener
6654   * @member Roo
6655   * @method onReady
6656  */
6657 Roo.onReady = Roo.EventManager.onDocumentReady;
6658
6659 Roo.onReady(function(){
6660     var bd = Roo.get(document.body);
6661     if(!bd){ return; }
6662
6663     var cls = [
6664             Roo.isIE ? "roo-ie"
6665             : Roo.isIE11 ? "roo-ie11"
6666             : Roo.isEdge ? "roo-edge"
6667             : Roo.isGecko ? "roo-gecko"
6668             : Roo.isOpera ? "roo-opera"
6669             : Roo.isSafari ? "roo-safari" : ""];
6670
6671     if(Roo.isMac){
6672         cls.push("roo-mac");
6673     }
6674     if(Roo.isLinux){
6675         cls.push("roo-linux");
6676     }
6677     if(Roo.isIOS){
6678         cls.push("roo-ios");
6679     }
6680     if(Roo.isTouch){
6681         cls.push("roo-touch");
6682     }
6683     if(Roo.isBorderBox){
6684         cls.push('roo-border-box');
6685     }
6686     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6687         var p = bd.dom.parentNode;
6688         if(p){
6689             p.className += ' roo-strict';
6690         }
6691     }
6692     bd.addClass(cls.join(' '));
6693 });
6694
6695 /**
6696  * @class Roo.EventObject
6697  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6698  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6699  * Example:
6700  * <pre><code>
6701  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6702     e.preventDefault();
6703     var target = e.getTarget();
6704     ...
6705  }
6706  var myDiv = Roo.get("myDiv");
6707  myDiv.on("click", handleClick);
6708  //or
6709  Roo.EventManager.on("myDiv", 'click', handleClick);
6710  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6711  </code></pre>
6712  * @singleton
6713  */
6714 Roo.EventObject = function(){
6715     
6716     var E = Roo.lib.Event;
6717     
6718     // safari keypress events for special keys return bad keycodes
6719     var safariKeys = {
6720         63234 : 37, // left
6721         63235 : 39, // right
6722         63232 : 38, // up
6723         63233 : 40, // down
6724         63276 : 33, // page up
6725         63277 : 34, // page down
6726         63272 : 46, // delete
6727         63273 : 36, // home
6728         63275 : 35  // end
6729     };
6730
6731     // normalize button clicks
6732     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6733                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6734
6735     Roo.EventObjectImpl = function(e){
6736         if(e){
6737             this.setEvent(e.browserEvent || e);
6738         }
6739     };
6740     Roo.EventObjectImpl.prototype = {
6741         /**
6742          * Used to fix doc tools.
6743          * @scope Roo.EventObject.prototype
6744          */
6745             
6746
6747         
6748         
6749         /** The normal browser event */
6750         browserEvent : null,
6751         /** The button pressed in a mouse event */
6752         button : -1,
6753         /** True if the shift key was down during the event */
6754         shiftKey : false,
6755         /** True if the control key was down during the event */
6756         ctrlKey : false,
6757         /** True if the alt key was down during the event */
6758         altKey : false,
6759
6760         /** Key constant 
6761         * @type Number */
6762         BACKSPACE : 8,
6763         /** Key constant 
6764         * @type Number */
6765         TAB : 9,
6766         /** Key constant 
6767         * @type Number */
6768         RETURN : 13,
6769         /** Key constant 
6770         * @type Number */
6771         ENTER : 13,
6772         /** Key constant 
6773         * @type Number */
6774         SHIFT : 16,
6775         /** Key constant 
6776         * @type Number */
6777         CONTROL : 17,
6778         /** Key constant 
6779         * @type Number */
6780         ESC : 27,
6781         /** Key constant 
6782         * @type Number */
6783         SPACE : 32,
6784         /** Key constant 
6785         * @type Number */
6786         PAGEUP : 33,
6787         /** Key constant 
6788         * @type Number */
6789         PAGEDOWN : 34,
6790         /** Key constant 
6791         * @type Number */
6792         END : 35,
6793         /** Key constant 
6794         * @type Number */
6795         HOME : 36,
6796         /** Key constant 
6797         * @type Number */
6798         LEFT : 37,
6799         /** Key constant 
6800         * @type Number */
6801         UP : 38,
6802         /** Key constant 
6803         * @type Number */
6804         RIGHT : 39,
6805         /** Key constant 
6806         * @type Number */
6807         DOWN : 40,
6808         /** Key constant 
6809         * @type Number */
6810         DELETE : 46,
6811         /** Key constant 
6812         * @type Number */
6813         F5 : 116,
6814
6815            /** @private */
6816         setEvent : function(e){
6817             if(e == this || (e && e.browserEvent)){ // already wrapped
6818                 return e;
6819             }
6820             this.browserEvent = e;
6821             if(e){
6822                 // normalize buttons
6823                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6824                 if(e.type == 'click' && this.button == -1){
6825                     this.button = 0;
6826                 }
6827                 this.type = e.type;
6828                 this.shiftKey = e.shiftKey;
6829                 // mac metaKey behaves like ctrlKey
6830                 this.ctrlKey = e.ctrlKey || e.metaKey;
6831                 this.altKey = e.altKey;
6832                 // in getKey these will be normalized for the mac
6833                 this.keyCode = e.keyCode;
6834                 // keyup warnings on firefox.
6835                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6836                 // cache the target for the delayed and or buffered events
6837                 this.target = E.getTarget(e);
6838                 // same for XY
6839                 this.xy = E.getXY(e);
6840             }else{
6841                 this.button = -1;
6842                 this.shiftKey = false;
6843                 this.ctrlKey = false;
6844                 this.altKey = false;
6845                 this.keyCode = 0;
6846                 this.charCode =0;
6847                 this.target = null;
6848                 this.xy = [0, 0];
6849             }
6850             return this;
6851         },
6852
6853         /**
6854          * Stop the event (preventDefault and stopPropagation)
6855          */
6856         stopEvent : function(){
6857             if(this.browserEvent){
6858                 if(this.browserEvent.type == 'mousedown'){
6859                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6860                 }
6861                 E.stopEvent(this.browserEvent);
6862             }
6863         },
6864
6865         /**
6866          * Prevents the browsers default handling of the event.
6867          */
6868         preventDefault : function(){
6869             if(this.browserEvent){
6870                 E.preventDefault(this.browserEvent);
6871             }
6872         },
6873
6874         /** @private */
6875         isNavKeyPress : function(){
6876             var k = this.keyCode;
6877             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6878             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6879         },
6880
6881         isSpecialKey : function(){
6882             var k = this.keyCode;
6883             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6884             (k == 16) || (k == 17) ||
6885             (k >= 18 && k <= 20) ||
6886             (k >= 33 && k <= 35) ||
6887             (k >= 36 && k <= 39) ||
6888             (k >= 44 && k <= 45);
6889         },
6890         /**
6891          * Cancels bubbling of the event.
6892          */
6893         stopPropagation : function(){
6894             if(this.browserEvent){
6895                 if(this.type == 'mousedown'){
6896                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6897                 }
6898                 E.stopPropagation(this.browserEvent);
6899             }
6900         },
6901
6902         /**
6903          * Gets the key code for the event.
6904          * @return {Number}
6905          */
6906         getCharCode : function(){
6907             return this.charCode || this.keyCode;
6908         },
6909
6910         /**
6911          * Returns a normalized keyCode for the event.
6912          * @return {Number} The key code
6913          */
6914         getKey : function(){
6915             var k = this.keyCode || this.charCode;
6916             return Roo.isSafari ? (safariKeys[k] || k) : k;
6917         },
6918
6919         /**
6920          * Gets the x coordinate of the event.
6921          * @return {Number}
6922          */
6923         getPageX : function(){
6924             return this.xy[0];
6925         },
6926
6927         /**
6928          * Gets the y coordinate of the event.
6929          * @return {Number}
6930          */
6931         getPageY : function(){
6932             return this.xy[1];
6933         },
6934
6935         /**
6936          * Gets the time of the event.
6937          * @return {Number}
6938          */
6939         getTime : function(){
6940             if(this.browserEvent){
6941                 return E.getTime(this.browserEvent);
6942             }
6943             return null;
6944         },
6945
6946         /**
6947          * Gets the page coordinates of the event.
6948          * @return {Array} The xy values like [x, y]
6949          */
6950         getXY : function(){
6951             return this.xy;
6952         },
6953
6954         /**
6955          * Gets the target for the event.
6956          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6957          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6958                 search as a number or element (defaults to 10 || document.body)
6959          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6960          * @return {HTMLelement}
6961          */
6962         getTarget : function(selector, maxDepth, returnEl){
6963             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6964         },
6965         /**
6966          * Gets the related target.
6967          * @return {HTMLElement}
6968          */
6969         getRelatedTarget : function(){
6970             if(this.browserEvent){
6971                 return E.getRelatedTarget(this.browserEvent);
6972             }
6973             return null;
6974         },
6975
6976         /**
6977          * Normalizes mouse wheel delta across browsers
6978          * @return {Number} The delta
6979          */
6980         getWheelDelta : function(){
6981             var e = this.browserEvent;
6982             var delta = 0;
6983             if(e.wheelDelta){ /* IE/Opera. */
6984                 delta = e.wheelDelta/120;
6985             }else if(e.detail){ /* Mozilla case. */
6986                 delta = -e.detail/3;
6987             }
6988             return delta;
6989         },
6990
6991         /**
6992          * Returns true if the control, meta, shift or alt key was pressed during this event.
6993          * @return {Boolean}
6994          */
6995         hasModifier : function(){
6996             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6997         },
6998
6999         /**
7000          * Returns true if the target of this event equals el or is a child of el
7001          * @param {String/HTMLElement/Element} el
7002          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7003          * @return {Boolean}
7004          */
7005         within : function(el, related){
7006             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7007             return t && Roo.fly(el).contains(t);
7008         },
7009
7010         getPoint : function(){
7011             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7012         }
7013     };
7014
7015     return new Roo.EventObjectImpl();
7016 }();
7017             
7018     /*
7019  * Based on:
7020  * Ext JS Library 1.1.1
7021  * Copyright(c) 2006-2007, Ext JS, LLC.
7022  *
7023  * Originally Released Under LGPL - original licence link has changed is not relivant.
7024  *
7025  * Fork - LGPL
7026  * <script type="text/javascript">
7027  */
7028
7029  
7030 // was in Composite Element!??!?!
7031  
7032 (function(){
7033     var D = Roo.lib.Dom;
7034     var E = Roo.lib.Event;
7035     var A = Roo.lib.Anim;
7036
7037     // local style camelizing for speed
7038     var propCache = {};
7039     var camelRe = /(-[a-z])/gi;
7040     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7041     var view = document.defaultView;
7042
7043 /**
7044  * @class Roo.Element
7045  * Represents an Element in the DOM.<br><br>
7046  * Usage:<br>
7047 <pre><code>
7048 var el = Roo.get("my-div");
7049
7050 // or with getEl
7051 var el = getEl("my-div");
7052
7053 // or with a DOM element
7054 var el = Roo.get(myDivElement);
7055 </code></pre>
7056  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7057  * each call instead of constructing a new one.<br><br>
7058  * <b>Animations</b><br />
7059  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7060  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7061 <pre>
7062 Option    Default   Description
7063 --------- --------  ---------------------------------------------
7064 duration  .35       The duration of the animation in seconds
7065 easing    easeOut   The YUI easing method
7066 callback  none      A function to execute when the anim completes
7067 scope     this      The scope (this) of the callback function
7068 </pre>
7069 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7070 * manipulate the animation. Here's an example:
7071 <pre><code>
7072 var el = Roo.get("my-div");
7073
7074 // no animation
7075 el.setWidth(100);
7076
7077 // default animation
7078 el.setWidth(100, true);
7079
7080 // animation with some options set
7081 el.setWidth(100, {
7082     duration: 1,
7083     callback: this.foo,
7084     scope: this
7085 });
7086
7087 // using the "anim" property to get the Anim object
7088 var opt = {
7089     duration: 1,
7090     callback: this.foo,
7091     scope: this
7092 };
7093 el.setWidth(100, opt);
7094 ...
7095 if(opt.anim.isAnimated()){
7096     opt.anim.stop();
7097 }
7098 </code></pre>
7099 * <b> Composite (Collections of) Elements</b><br />
7100  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7101  * @constructor Create a new Element directly.
7102  * @param {String/HTMLElement} element
7103  * @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).
7104  */
7105     Roo.Element = function(element, forceNew){
7106         var dom = typeof element == "string" ?
7107                 document.getElementById(element) : element;
7108         if(!dom){ // invalid id/element
7109             return null;
7110         }
7111         var id = dom.id;
7112         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7113             return Roo.Element.cache[id];
7114         }
7115
7116         /**
7117          * The DOM element
7118          * @type HTMLElement
7119          */
7120         this.dom = dom;
7121
7122         /**
7123          * The DOM element ID
7124          * @type String
7125          */
7126         this.id = id || Roo.id(dom);
7127     };
7128
7129     var El = Roo.Element;
7130
7131     El.prototype = {
7132         /**
7133          * The element's default display mode  (defaults to "")
7134          * @type String
7135          */
7136         originalDisplay : "",
7137
7138         visibilityMode : 1,
7139         /**
7140          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7141          * @type String
7142          */
7143         defaultUnit : "px",
7144         
7145         /**
7146          * Sets the element's visibility mode. When setVisible() is called it
7147          * will use this to determine whether to set the visibility or the display property.
7148          * @param visMode Element.VISIBILITY or Element.DISPLAY
7149          * @return {Roo.Element} this
7150          */
7151         setVisibilityMode : function(visMode){
7152             this.visibilityMode = visMode;
7153             return this;
7154         },
7155         /**
7156          * Convenience method for setVisibilityMode(Element.DISPLAY)
7157          * @param {String} display (optional) What to set display to when visible
7158          * @return {Roo.Element} this
7159          */
7160         enableDisplayMode : function(display){
7161             this.setVisibilityMode(El.DISPLAY);
7162             if(typeof display != "undefined") { this.originalDisplay = display; }
7163             return this;
7164         },
7165
7166         /**
7167          * 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)
7168          * @param {String} selector The simple selector to test
7169          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7170                 search as a number or element (defaults to 10 || document.body)
7171          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7172          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7173          */
7174         findParent : function(simpleSelector, maxDepth, returnEl){
7175             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7176             maxDepth = maxDepth || 50;
7177             if(typeof maxDepth != "number"){
7178                 stopEl = Roo.getDom(maxDepth);
7179                 maxDepth = 10;
7180             }
7181             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7182                 if(dq.is(p, simpleSelector)){
7183                     return returnEl ? Roo.get(p) : p;
7184                 }
7185                 depth++;
7186                 p = p.parentNode;
7187             }
7188             return null;
7189         },
7190
7191
7192         /**
7193          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7194          * @param {String} selector The simple selector to test
7195          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7196                 search as a number or element (defaults to 10 || document.body)
7197          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7198          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7199          */
7200         findParentNode : function(simpleSelector, maxDepth, returnEl){
7201             var p = Roo.fly(this.dom.parentNode, '_internal');
7202             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7203         },
7204         
7205         /**
7206          * Looks at  the scrollable parent element
7207          */
7208         findScrollableParent : function()
7209         {
7210             var overflowRegex = /(auto|scroll)/;
7211             
7212             if(this.getStyle('position') === 'fixed'){
7213                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7214             }
7215             
7216             var excludeStaticParent = this.getStyle('position') === "absolute";
7217             
7218             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7219                 
7220                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7221                     continue;
7222                 }
7223                 
7224                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7225                     return parent;
7226                 }
7227                 
7228                 if(parent.dom.nodeName.toLowerCase() == 'body'){
7229                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7230                 }
7231             }
7232             
7233             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7234         },
7235
7236         /**
7237          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7238          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7239          * @param {String} selector The simple selector to test
7240          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7241                 search as a number or element (defaults to 10 || document.body)
7242          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7243          */
7244         up : function(simpleSelector, maxDepth){
7245             return this.findParentNode(simpleSelector, maxDepth, true);
7246         },
7247
7248
7249
7250         /**
7251          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7252          * @param {String} selector The simple selector to test
7253          * @return {Boolean} True if this element matches the selector, else false
7254          */
7255         is : function(simpleSelector){
7256             return Roo.DomQuery.is(this.dom, simpleSelector);
7257         },
7258
7259         /**
7260          * Perform animation on this element.
7261          * @param {Object} args The YUI animation control args
7262          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7263          * @param {Function} onComplete (optional) Function to call when animation completes
7264          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7265          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7266          * @return {Roo.Element} this
7267          */
7268         animate : function(args, duration, onComplete, easing, animType){
7269             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7270             return this;
7271         },
7272
7273         /*
7274          * @private Internal animation call
7275          */
7276         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7277             animType = animType || 'run';
7278             opt = opt || {};
7279             var anim = Roo.lib.Anim[animType](
7280                 this.dom, args,
7281                 (opt.duration || defaultDur) || .35,
7282                 (opt.easing || defaultEase) || 'easeOut',
7283                 function(){
7284                     Roo.callback(cb, this);
7285                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7286                 },
7287                 this
7288             );
7289             opt.anim = anim;
7290             return anim;
7291         },
7292
7293         // private legacy anim prep
7294         preanim : function(a, i){
7295             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7296         },
7297
7298         /**
7299          * Removes worthless text nodes
7300          * @param {Boolean} forceReclean (optional) By default the element
7301          * keeps track if it has been cleaned already so
7302          * you can call this over and over. However, if you update the element and
7303          * need to force a reclean, you can pass true.
7304          */
7305         clean : function(forceReclean){
7306             if(this.isCleaned && forceReclean !== true){
7307                 return this;
7308             }
7309             var ns = /\S/;
7310             var d = this.dom, n = d.firstChild, ni = -1;
7311             while(n){
7312                 var nx = n.nextSibling;
7313                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7314                     d.removeChild(n);
7315                 }else{
7316                     n.nodeIndex = ++ni;
7317                 }
7318                 n = nx;
7319             }
7320             this.isCleaned = true;
7321             return this;
7322         },
7323
7324         // private
7325         calcOffsetsTo : function(el){
7326             el = Roo.get(el);
7327             var d = el.dom;
7328             var restorePos = false;
7329             if(el.getStyle('position') == 'static'){
7330                 el.position('relative');
7331                 restorePos = true;
7332             }
7333             var x = 0, y =0;
7334             var op = this.dom;
7335             while(op && op != d && op.tagName != 'HTML'){
7336                 x+= op.offsetLeft;
7337                 y+= op.offsetTop;
7338                 op = op.offsetParent;
7339             }
7340             if(restorePos){
7341                 el.position('static');
7342             }
7343             return [x, y];
7344         },
7345
7346         /**
7347          * Scrolls this element into view within the passed container.
7348          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7349          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7350          * @return {Roo.Element} this
7351          */
7352         scrollIntoView : function(container, hscroll){
7353             var c = Roo.getDom(container) || document.body;
7354             var el = this.dom;
7355
7356             var o = this.calcOffsetsTo(c),
7357                 l = o[0],
7358                 t = o[1],
7359                 b = t+el.offsetHeight,
7360                 r = l+el.offsetWidth;
7361
7362             var ch = c.clientHeight;
7363             var ct = parseInt(c.scrollTop, 10);
7364             var cl = parseInt(c.scrollLeft, 10);
7365             var cb = ct + ch;
7366             var cr = cl + c.clientWidth;
7367
7368             if(t < ct){
7369                 c.scrollTop = t;
7370             }else if(b > cb){
7371                 c.scrollTop = b-ch;
7372             }
7373
7374             if(hscroll !== false){
7375                 if(l < cl){
7376                     c.scrollLeft = l;
7377                 }else if(r > cr){
7378                     c.scrollLeft = r-c.clientWidth;
7379                 }
7380             }
7381             return this;
7382         },
7383
7384         // private
7385         scrollChildIntoView : function(child, hscroll){
7386             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7387         },
7388
7389         /**
7390          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7391          * the new height may not be available immediately.
7392          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7393          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7394          * @param {Function} onComplete (optional) Function to call when animation completes
7395          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7396          * @return {Roo.Element} this
7397          */
7398         autoHeight : function(animate, duration, onComplete, easing){
7399             var oldHeight = this.getHeight();
7400             this.clip();
7401             this.setHeight(1); // force clipping
7402             setTimeout(function(){
7403                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7404                 if(!animate){
7405                     this.setHeight(height);
7406                     this.unclip();
7407                     if(typeof onComplete == "function"){
7408                         onComplete();
7409                     }
7410                 }else{
7411                     this.setHeight(oldHeight); // restore original height
7412                     this.setHeight(height, animate, duration, function(){
7413                         this.unclip();
7414                         if(typeof onComplete == "function") { onComplete(); }
7415                     }.createDelegate(this), easing);
7416                 }
7417             }.createDelegate(this), 0);
7418             return this;
7419         },
7420
7421         /**
7422          * Returns true if this element is an ancestor of the passed element
7423          * @param {HTMLElement/String} el The element to check
7424          * @return {Boolean} True if this element is an ancestor of el, else false
7425          */
7426         contains : function(el){
7427             if(!el){return false;}
7428             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7429         },
7430
7431         /**
7432          * Checks whether the element is currently visible using both visibility and display properties.
7433          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7434          * @return {Boolean} True if the element is currently visible, else false
7435          */
7436         isVisible : function(deep) {
7437             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7438             if(deep !== true || !vis){
7439                 return vis;
7440             }
7441             var p = this.dom.parentNode;
7442             while(p && p.tagName.toLowerCase() != "body"){
7443                 if(!Roo.fly(p, '_isVisible').isVisible()){
7444                     return false;
7445                 }
7446                 p = p.parentNode;
7447             }
7448             return true;
7449         },
7450
7451         /**
7452          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7453          * @param {String} selector The CSS selector
7454          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7455          * @return {CompositeElement/CompositeElementLite} The composite element
7456          */
7457         select : function(selector, unique){
7458             return El.select(selector, unique, this.dom);
7459         },
7460
7461         /**
7462          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7463          * @param {String} selector The CSS selector
7464          * @return {Array} An array of the matched nodes
7465          */
7466         query : function(selector, unique){
7467             return Roo.DomQuery.select(selector, this.dom);
7468         },
7469
7470         /**
7471          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7472          * @param {String} selector The CSS selector
7473          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7474          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7475          */
7476         child : function(selector, returnDom){
7477             var n = Roo.DomQuery.selectNode(selector, this.dom);
7478             return returnDom ? n : Roo.get(n);
7479         },
7480
7481         /**
7482          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7483          * @param {String} selector The CSS selector
7484          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7485          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7486          */
7487         down : function(selector, returnDom){
7488             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7489             return returnDom ? n : Roo.get(n);
7490         },
7491
7492         /**
7493          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7494          * @param {String} group The group the DD object is member of
7495          * @param {Object} config The DD config object
7496          * @param {Object} overrides An object containing methods to override/implement on the DD object
7497          * @return {Roo.dd.DD} The DD object
7498          */
7499         initDD : function(group, config, overrides){
7500             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7501             return Roo.apply(dd, overrides);
7502         },
7503
7504         /**
7505          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7506          * @param {String} group The group the DDProxy object is member of
7507          * @param {Object} config The DDProxy config object
7508          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7509          * @return {Roo.dd.DDProxy} The DDProxy object
7510          */
7511         initDDProxy : function(group, config, overrides){
7512             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7513             return Roo.apply(dd, overrides);
7514         },
7515
7516         /**
7517          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7518          * @param {String} group The group the DDTarget object is member of
7519          * @param {Object} config The DDTarget config object
7520          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7521          * @return {Roo.dd.DDTarget} The DDTarget object
7522          */
7523         initDDTarget : function(group, config, overrides){
7524             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7525             return Roo.apply(dd, overrides);
7526         },
7527
7528         /**
7529          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7530          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7531          * @param {Boolean} visible Whether the element is visible
7532          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7533          * @return {Roo.Element} this
7534          */
7535          setVisible : function(visible, animate){
7536             if(!animate || !A){
7537                 if(this.visibilityMode == El.DISPLAY){
7538                     this.setDisplayed(visible);
7539                 }else{
7540                     this.fixDisplay();
7541                     this.dom.style.visibility = visible ? "visible" : "hidden";
7542                 }
7543             }else{
7544                 // closure for composites
7545                 var dom = this.dom;
7546                 var visMode = this.visibilityMode;
7547                 if(visible){
7548                     this.setOpacity(.01);
7549                     this.setVisible(true);
7550                 }
7551                 this.anim({opacity: { to: (visible?1:0) }},
7552                       this.preanim(arguments, 1),
7553                       null, .35, 'easeIn', function(){
7554                          if(!visible){
7555                              if(visMode == El.DISPLAY){
7556                                  dom.style.display = "none";
7557                              }else{
7558                                  dom.style.visibility = "hidden";
7559                              }
7560                              Roo.get(dom).setOpacity(1);
7561                          }
7562                      });
7563             }
7564             return this;
7565         },
7566
7567         /**
7568          * Returns true if display is not "none"
7569          * @return {Boolean}
7570          */
7571         isDisplayed : function() {
7572             return this.getStyle("display") != "none";
7573         },
7574
7575         /**
7576          * Toggles the element's visibility or display, depending on visibility mode.
7577          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7578          * @return {Roo.Element} this
7579          */
7580         toggle : function(animate){
7581             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7582             return this;
7583         },
7584
7585         /**
7586          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7587          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7588          * @return {Roo.Element} this
7589          */
7590         setDisplayed : function(value) {
7591             if(typeof value == "boolean"){
7592                value = value ? this.originalDisplay : "none";
7593             }
7594             this.setStyle("display", value);
7595             return this;
7596         },
7597
7598         /**
7599          * Tries to focus the element. Any exceptions are caught and ignored.
7600          * @return {Roo.Element} this
7601          */
7602         focus : function() {
7603             try{
7604                 this.dom.focus();
7605             }catch(e){}
7606             return this;
7607         },
7608
7609         /**
7610          * Tries to blur the element. Any exceptions are caught and ignored.
7611          * @return {Roo.Element} this
7612          */
7613         blur : function() {
7614             try{
7615                 this.dom.blur();
7616             }catch(e){}
7617             return this;
7618         },
7619
7620         /**
7621          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7622          * @param {String/Array} className The CSS class to add, or an array of classes
7623          * @return {Roo.Element} this
7624          */
7625         addClass : function(className){
7626             if(className instanceof Array){
7627                 for(var i = 0, len = className.length; i < len; i++) {
7628                     this.addClass(className[i]);
7629                 }
7630             }else{
7631                 if(className && !this.hasClass(className)){
7632                     this.dom.className = this.dom.className + " " + className;
7633                 }
7634             }
7635             return this;
7636         },
7637
7638         /**
7639          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7640          * @param {String/Array} className The CSS class to add, or an array of classes
7641          * @return {Roo.Element} this
7642          */
7643         radioClass : function(className){
7644             var siblings = this.dom.parentNode.childNodes;
7645             for(var i = 0; i < siblings.length; i++) {
7646                 var s = siblings[i];
7647                 if(s.nodeType == 1){
7648                     Roo.get(s).removeClass(className);
7649                 }
7650             }
7651             this.addClass(className);
7652             return this;
7653         },
7654
7655         /**
7656          * Removes one or more CSS classes from the element.
7657          * @param {String/Array} className The CSS class to remove, or an array of classes
7658          * @return {Roo.Element} this
7659          */
7660         removeClass : function(className){
7661             if(!className || !this.dom.className){
7662                 return this;
7663             }
7664             if(className instanceof Array){
7665                 for(var i = 0, len = className.length; i < len; i++) {
7666                     this.removeClass(className[i]);
7667                 }
7668             }else{
7669                 if(this.hasClass(className)){
7670                     var re = this.classReCache[className];
7671                     if (!re) {
7672                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7673                        this.classReCache[className] = re;
7674                     }
7675                     this.dom.className =
7676                         this.dom.className.replace(re, " ");
7677                 }
7678             }
7679             return this;
7680         },
7681
7682         // private
7683         classReCache: {},
7684
7685         /**
7686          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7687          * @param {String} className The CSS class to toggle
7688          * @return {Roo.Element} this
7689          */
7690         toggleClass : function(className){
7691             if(this.hasClass(className)){
7692                 this.removeClass(className);
7693             }else{
7694                 this.addClass(className);
7695             }
7696             return this;
7697         },
7698
7699         /**
7700          * Checks if the specified CSS class exists on this element's DOM node.
7701          * @param {String} className The CSS class to check for
7702          * @return {Boolean} True if the class exists, else false
7703          */
7704         hasClass : function(className){
7705             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7706         },
7707
7708         /**
7709          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7710          * @param {String} oldClassName The CSS class to replace
7711          * @param {String} newClassName The replacement CSS class
7712          * @return {Roo.Element} this
7713          */
7714         replaceClass : function(oldClassName, newClassName){
7715             this.removeClass(oldClassName);
7716             this.addClass(newClassName);
7717             return this;
7718         },
7719
7720         /**
7721          * Returns an object with properties matching the styles requested.
7722          * For example, el.getStyles('color', 'font-size', 'width') might return
7723          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7724          * @param {String} style1 A style name
7725          * @param {String} style2 A style name
7726          * @param {String} etc.
7727          * @return {Object} The style object
7728          */
7729         getStyles : function(){
7730             var a = arguments, len = a.length, r = {};
7731             for(var i = 0; i < len; i++){
7732                 r[a[i]] = this.getStyle(a[i]);
7733             }
7734             return r;
7735         },
7736
7737         /**
7738          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7739          * @param {String} property The style property whose value is returned.
7740          * @return {String} The current value of the style property for this element.
7741          */
7742         getStyle : function(){
7743             return view && view.getComputedStyle ?
7744                 function(prop){
7745                     var el = this.dom, v, cs, camel;
7746                     if(prop == 'float'){
7747                         prop = "cssFloat";
7748                     }
7749                     if(el.style && (v = el.style[prop])){
7750                         return v;
7751                     }
7752                     if(cs = view.getComputedStyle(el, "")){
7753                         if(!(camel = propCache[prop])){
7754                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7755                         }
7756                         return cs[camel];
7757                     }
7758                     return null;
7759                 } :
7760                 function(prop){
7761                     var el = this.dom, v, cs, camel;
7762                     if(prop == 'opacity'){
7763                         if(typeof el.style.filter == 'string'){
7764                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7765                             if(m){
7766                                 var fv = parseFloat(m[1]);
7767                                 if(!isNaN(fv)){
7768                                     return fv ? fv / 100 : 0;
7769                                 }
7770                             }
7771                         }
7772                         return 1;
7773                     }else if(prop == 'float'){
7774                         prop = "styleFloat";
7775                     }
7776                     if(!(camel = propCache[prop])){
7777                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7778                     }
7779                     if(v = el.style[camel]){
7780                         return v;
7781                     }
7782                     if(cs = el.currentStyle){
7783                         return cs[camel];
7784                     }
7785                     return null;
7786                 };
7787         }(),
7788
7789         /**
7790          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7791          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7792          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7793          * @return {Roo.Element} this
7794          */
7795         setStyle : function(prop, value){
7796             if(typeof prop == "string"){
7797                 
7798                 if (prop == 'float') {
7799                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7800                     return this;
7801                 }
7802                 
7803                 var camel;
7804                 if(!(camel = propCache[prop])){
7805                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7806                 }
7807                 
7808                 if(camel == 'opacity') {
7809                     this.setOpacity(value);
7810                 }else{
7811                     this.dom.style[camel] = value;
7812                 }
7813             }else{
7814                 for(var style in prop){
7815                     if(typeof prop[style] != "function"){
7816                        this.setStyle(style, prop[style]);
7817                     }
7818                 }
7819             }
7820             return this;
7821         },
7822
7823         /**
7824          * More flexible version of {@link #setStyle} for setting style properties.
7825          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7826          * a function which returns such a specification.
7827          * @return {Roo.Element} this
7828          */
7829         applyStyles : function(style){
7830             Roo.DomHelper.applyStyles(this.dom, style);
7831             return this;
7832         },
7833
7834         /**
7835           * 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).
7836           * @return {Number} The X position of the element
7837           */
7838         getX : function(){
7839             return D.getX(this.dom);
7840         },
7841
7842         /**
7843           * 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).
7844           * @return {Number} The Y position of the element
7845           */
7846         getY : function(){
7847             return D.getY(this.dom);
7848         },
7849
7850         /**
7851           * 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).
7852           * @return {Array} The XY position of the element
7853           */
7854         getXY : function(){
7855             return D.getXY(this.dom);
7856         },
7857
7858         /**
7859          * 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).
7860          * @param {Number} The X position of the element
7861          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7862          * @return {Roo.Element} this
7863          */
7864         setX : function(x, animate){
7865             if(!animate || !A){
7866                 D.setX(this.dom, x);
7867             }else{
7868                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7869             }
7870             return this;
7871         },
7872
7873         /**
7874          * 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).
7875          * @param {Number} The Y position of the element
7876          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7877          * @return {Roo.Element} this
7878          */
7879         setY : function(y, animate){
7880             if(!animate || !A){
7881                 D.setY(this.dom, y);
7882             }else{
7883                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7884             }
7885             return this;
7886         },
7887
7888         /**
7889          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7890          * @param {String} left The left CSS property value
7891          * @return {Roo.Element} this
7892          */
7893         setLeft : function(left){
7894             this.setStyle("left", this.addUnits(left));
7895             return this;
7896         },
7897
7898         /**
7899          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7900          * @param {String} top The top CSS property value
7901          * @return {Roo.Element} this
7902          */
7903         setTop : function(top){
7904             this.setStyle("top", this.addUnits(top));
7905             return this;
7906         },
7907
7908         /**
7909          * Sets the element's CSS right style.
7910          * @param {String} right The right CSS property value
7911          * @return {Roo.Element} this
7912          */
7913         setRight : function(right){
7914             this.setStyle("right", this.addUnits(right));
7915             return this;
7916         },
7917
7918         /**
7919          * Sets the element's CSS bottom style.
7920          * @param {String} bottom The bottom CSS property value
7921          * @return {Roo.Element} this
7922          */
7923         setBottom : function(bottom){
7924             this.setStyle("bottom", this.addUnits(bottom));
7925             return this;
7926         },
7927
7928         /**
7929          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7930          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7931          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7932          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7933          * @return {Roo.Element} this
7934          */
7935         setXY : function(pos, animate){
7936             if(!animate || !A){
7937                 D.setXY(this.dom, pos);
7938             }else{
7939                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7940             }
7941             return this;
7942         },
7943
7944         /**
7945          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7946          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7947          * @param {Number} x X value for new position (coordinates are page-based)
7948          * @param {Number} y Y value for new position (coordinates are page-based)
7949          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7950          * @return {Roo.Element} this
7951          */
7952         setLocation : function(x, y, animate){
7953             this.setXY([x, y], this.preanim(arguments, 2));
7954             return this;
7955         },
7956
7957         /**
7958          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7959          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7960          * @param {Number} x X value for new position (coordinates are page-based)
7961          * @param {Number} y Y value for new position (coordinates are page-based)
7962          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7963          * @return {Roo.Element} this
7964          */
7965         moveTo : function(x, y, animate){
7966             this.setXY([x, y], this.preanim(arguments, 2));
7967             return this;
7968         },
7969
7970         /**
7971          * Returns the region of the given element.
7972          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7973          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7974          */
7975         getRegion : function(){
7976             return D.getRegion(this.dom);
7977         },
7978
7979         /**
7980          * Returns the offset height of the element
7981          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7982          * @return {Number} The element's height
7983          */
7984         getHeight : function(contentHeight){
7985             var h = this.dom.offsetHeight || 0;
7986             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7987         },
7988
7989         /**
7990          * Returns the offset width of the element
7991          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7992          * @return {Number} The element's width
7993          */
7994         getWidth : function(contentWidth){
7995             var w = this.dom.offsetWidth || 0;
7996             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7997         },
7998
7999         /**
8000          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8001          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8002          * if a height has not been set using CSS.
8003          * @return {Number}
8004          */
8005         getComputedHeight : function(){
8006             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8007             if(!h){
8008                 h = parseInt(this.getStyle('height'), 10) || 0;
8009                 if(!this.isBorderBox()){
8010                     h += this.getFrameWidth('tb');
8011                 }
8012             }
8013             return h;
8014         },
8015
8016         /**
8017          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8018          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8019          * if a width has not been set using CSS.
8020          * @return {Number}
8021          */
8022         getComputedWidth : function(){
8023             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8024             if(!w){
8025                 w = parseInt(this.getStyle('width'), 10) || 0;
8026                 if(!this.isBorderBox()){
8027                     w += this.getFrameWidth('lr');
8028                 }
8029             }
8030             return w;
8031         },
8032
8033         /**
8034          * Returns the size of the element.
8035          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8036          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8037          */
8038         getSize : function(contentSize){
8039             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8040         },
8041
8042         /**
8043          * Returns the width and height of the viewport.
8044          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8045          */
8046         getViewSize : function(){
8047             var d = this.dom, doc = document, aw = 0, ah = 0;
8048             if(d == doc || d == doc.body){
8049                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8050             }else{
8051                 return {
8052                     width : d.clientWidth,
8053                     height: d.clientHeight
8054                 };
8055             }
8056         },
8057
8058         /**
8059          * Returns the value of the "value" attribute
8060          * @param {Boolean} asNumber true to parse the value as a number
8061          * @return {String/Number}
8062          */
8063         getValue : function(asNumber){
8064             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8065         },
8066
8067         // private
8068         adjustWidth : function(width){
8069             if(typeof width == "number"){
8070                 if(this.autoBoxAdjust && !this.isBorderBox()){
8071                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8072                 }
8073                 if(width < 0){
8074                     width = 0;
8075                 }
8076             }
8077             return width;
8078         },
8079
8080         // private
8081         adjustHeight : function(height){
8082             if(typeof height == "number"){
8083                if(this.autoBoxAdjust && !this.isBorderBox()){
8084                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8085                }
8086                if(height < 0){
8087                    height = 0;
8088                }
8089             }
8090             return height;
8091         },
8092
8093         /**
8094          * Set the width of the element
8095          * @param {Number} width The new width
8096          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8097          * @return {Roo.Element} this
8098          */
8099         setWidth : function(width, animate){
8100             width = this.adjustWidth(width);
8101             if(!animate || !A){
8102                 this.dom.style.width = this.addUnits(width);
8103             }else{
8104                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8105             }
8106             return this;
8107         },
8108
8109         /**
8110          * Set the height of the element
8111          * @param {Number} height The new height
8112          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8113          * @return {Roo.Element} this
8114          */
8115          setHeight : function(height, animate){
8116             height = this.adjustHeight(height);
8117             if(!animate || !A){
8118                 this.dom.style.height = this.addUnits(height);
8119             }else{
8120                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8121             }
8122             return this;
8123         },
8124
8125         /**
8126          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8127          * @param {Number} width The new width
8128          * @param {Number} height The new height
8129          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8130          * @return {Roo.Element} this
8131          */
8132          setSize : function(width, height, animate){
8133             if(typeof width == "object"){ // in case of object from getSize()
8134                 height = width.height; width = width.width;
8135             }
8136             width = this.adjustWidth(width); height = this.adjustHeight(height);
8137             if(!animate || !A){
8138                 this.dom.style.width = this.addUnits(width);
8139                 this.dom.style.height = this.addUnits(height);
8140             }else{
8141                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8142             }
8143             return this;
8144         },
8145
8146         /**
8147          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8148          * @param {Number} x X value for new position (coordinates are page-based)
8149          * @param {Number} y Y value for new position (coordinates are page-based)
8150          * @param {Number} width The new width
8151          * @param {Number} height The new height
8152          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8153          * @return {Roo.Element} this
8154          */
8155         setBounds : function(x, y, width, height, animate){
8156             if(!animate || !A){
8157                 this.setSize(width, height);
8158                 this.setLocation(x, y);
8159             }else{
8160                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8161                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8162                               this.preanim(arguments, 4), 'motion');
8163             }
8164             return this;
8165         },
8166
8167         /**
8168          * 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.
8169          * @param {Roo.lib.Region} region The region to fill
8170          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8171          * @return {Roo.Element} this
8172          */
8173         setRegion : function(region, animate){
8174             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8175             return this;
8176         },
8177
8178         /**
8179          * Appends an event handler
8180          *
8181          * @param {String}   eventName     The type of event to append
8182          * @param {Function} fn        The method the event invokes
8183          * @param {Object} scope       (optional) The scope (this object) of the fn
8184          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8185          */
8186         addListener : function(eventName, fn, scope, options){
8187             if (this.dom) {
8188                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8189             }
8190         },
8191
8192         /**
8193          * Removes an event handler from this element
8194          * @param {String} eventName the type of event to remove
8195          * @param {Function} fn the method the event invokes
8196          * @return {Roo.Element} this
8197          */
8198         removeListener : function(eventName, fn){
8199             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8200             return this;
8201         },
8202
8203         /**
8204          * Removes all previous added listeners from this element
8205          * @return {Roo.Element} this
8206          */
8207         removeAllListeners : function(){
8208             E.purgeElement(this.dom);
8209             return this;
8210         },
8211
8212         relayEvent : function(eventName, observable){
8213             this.on(eventName, function(e){
8214                 observable.fireEvent(eventName, e);
8215             });
8216         },
8217
8218         /**
8219          * Set the opacity of the element
8220          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8221          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8222          * @return {Roo.Element} this
8223          */
8224          setOpacity : function(opacity, animate){
8225             if(!animate || !A){
8226                 var s = this.dom.style;
8227                 if(Roo.isIE){
8228                     s.zoom = 1;
8229                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8230                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8231                 }else{
8232                     s.opacity = opacity;
8233                 }
8234             }else{
8235                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8236             }
8237             return this;
8238         },
8239
8240         /**
8241          * Gets the left X coordinate
8242          * @param {Boolean} local True to get the local css position instead of page coordinate
8243          * @return {Number}
8244          */
8245         getLeft : function(local){
8246             if(!local){
8247                 return this.getX();
8248             }else{
8249                 return parseInt(this.getStyle("left"), 10) || 0;
8250             }
8251         },
8252
8253         /**
8254          * Gets the right X coordinate of the element (element X position + element width)
8255          * @param {Boolean} local True to get the local css position instead of page coordinate
8256          * @return {Number}
8257          */
8258         getRight : function(local){
8259             if(!local){
8260                 return this.getX() + this.getWidth();
8261             }else{
8262                 return (this.getLeft(true) + this.getWidth()) || 0;
8263             }
8264         },
8265
8266         /**
8267          * Gets the top Y coordinate
8268          * @param {Boolean} local True to get the local css position instead of page coordinate
8269          * @return {Number}
8270          */
8271         getTop : function(local) {
8272             if(!local){
8273                 return this.getY();
8274             }else{
8275                 return parseInt(this.getStyle("top"), 10) || 0;
8276             }
8277         },
8278
8279         /**
8280          * Gets the bottom Y coordinate of the element (element Y position + element height)
8281          * @param {Boolean} local True to get the local css position instead of page coordinate
8282          * @return {Number}
8283          */
8284         getBottom : function(local){
8285             if(!local){
8286                 return this.getY() + this.getHeight();
8287             }else{
8288                 return (this.getTop(true) + this.getHeight()) || 0;
8289             }
8290         },
8291
8292         /**
8293         * Initializes positioning on this element. If a desired position is not passed, it will make the
8294         * the element positioned relative IF it is not already positioned.
8295         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8296         * @param {Number} zIndex (optional) The zIndex to apply
8297         * @param {Number} x (optional) Set the page X position
8298         * @param {Number} y (optional) Set the page Y position
8299         */
8300         position : function(pos, zIndex, x, y){
8301             if(!pos){
8302                if(this.getStyle('position') == 'static'){
8303                    this.setStyle('position', 'relative');
8304                }
8305             }else{
8306                 this.setStyle("position", pos);
8307             }
8308             if(zIndex){
8309                 this.setStyle("z-index", zIndex);
8310             }
8311             if(x !== undefined && y !== undefined){
8312                 this.setXY([x, y]);
8313             }else if(x !== undefined){
8314                 this.setX(x);
8315             }else if(y !== undefined){
8316                 this.setY(y);
8317             }
8318         },
8319
8320         /**
8321         * Clear positioning back to the default when the document was loaded
8322         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8323         * @return {Roo.Element} this
8324          */
8325         clearPositioning : function(value){
8326             value = value ||'';
8327             this.setStyle({
8328                 "left": value,
8329                 "right": value,
8330                 "top": value,
8331                 "bottom": value,
8332                 "z-index": "",
8333                 "position" : "static"
8334             });
8335             return this;
8336         },
8337
8338         /**
8339         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8340         * snapshot before performing an update and then restoring the element.
8341         * @return {Object}
8342         */
8343         getPositioning : function(){
8344             var l = this.getStyle("left");
8345             var t = this.getStyle("top");
8346             return {
8347                 "position" : this.getStyle("position"),
8348                 "left" : l,
8349                 "right" : l ? "" : this.getStyle("right"),
8350                 "top" : t,
8351                 "bottom" : t ? "" : this.getStyle("bottom"),
8352                 "z-index" : this.getStyle("z-index")
8353             };
8354         },
8355
8356         /**
8357          * Gets the width of the border(s) for the specified side(s)
8358          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8359          * passing lr would get the border (l)eft width + the border (r)ight width.
8360          * @return {Number} The width of the sides passed added together
8361          */
8362         getBorderWidth : function(side){
8363             return this.addStyles(side, El.borders);
8364         },
8365
8366         /**
8367          * Gets the width of the padding(s) for the specified side(s)
8368          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8369          * passing lr would get the padding (l)eft + the padding (r)ight.
8370          * @return {Number} The padding of the sides passed added together
8371          */
8372         getPadding : function(side){
8373             return this.addStyles(side, El.paddings);
8374         },
8375
8376         /**
8377         * Set positioning with an object returned by getPositioning().
8378         * @param {Object} posCfg
8379         * @return {Roo.Element} this
8380          */
8381         setPositioning : function(pc){
8382             this.applyStyles(pc);
8383             if(pc.right == "auto"){
8384                 this.dom.style.right = "";
8385             }
8386             if(pc.bottom == "auto"){
8387                 this.dom.style.bottom = "";
8388             }
8389             return this;
8390         },
8391
8392         // private
8393         fixDisplay : function(){
8394             if(this.getStyle("display") == "none"){
8395                 this.setStyle("visibility", "hidden");
8396                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8397                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8398                     this.setStyle("display", "block");
8399                 }
8400             }
8401         },
8402
8403         /**
8404          * Quick set left and top adding default units
8405          * @param {String} left The left CSS property value
8406          * @param {String} top The top CSS property value
8407          * @return {Roo.Element} this
8408          */
8409          setLeftTop : function(left, top){
8410             this.dom.style.left = this.addUnits(left);
8411             this.dom.style.top = this.addUnits(top);
8412             return this;
8413         },
8414
8415         /**
8416          * Move this element relative to its current position.
8417          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8418          * @param {Number} distance How far to move the element in pixels
8419          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8420          * @return {Roo.Element} this
8421          */
8422          move : function(direction, distance, animate){
8423             var xy = this.getXY();
8424             direction = direction.toLowerCase();
8425             switch(direction){
8426                 case "l":
8427                 case "left":
8428                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8429                     break;
8430                case "r":
8431                case "right":
8432                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8433                     break;
8434                case "t":
8435                case "top":
8436                case "up":
8437                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8438                     break;
8439                case "b":
8440                case "bottom":
8441                case "down":
8442                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8443                     break;
8444             }
8445             return this;
8446         },
8447
8448         /**
8449          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8450          * @return {Roo.Element} this
8451          */
8452         clip : function(){
8453             if(!this.isClipped){
8454                this.isClipped = true;
8455                this.originalClip = {
8456                    "o": this.getStyle("overflow"),
8457                    "x": this.getStyle("overflow-x"),
8458                    "y": this.getStyle("overflow-y")
8459                };
8460                this.setStyle("overflow", "hidden");
8461                this.setStyle("overflow-x", "hidden");
8462                this.setStyle("overflow-y", "hidden");
8463             }
8464             return this;
8465         },
8466
8467         /**
8468          *  Return clipping (overflow) to original clipping before clip() was called
8469          * @return {Roo.Element} this
8470          */
8471         unclip : function(){
8472             if(this.isClipped){
8473                 this.isClipped = false;
8474                 var o = this.originalClip;
8475                 if(o.o){this.setStyle("overflow", o.o);}
8476                 if(o.x){this.setStyle("overflow-x", o.x);}
8477                 if(o.y){this.setStyle("overflow-y", o.y);}
8478             }
8479             return this;
8480         },
8481
8482
8483         /**
8484          * Gets the x,y coordinates specified by the anchor position on the element.
8485          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8486          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8487          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8488          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8489          * @return {Array} [x, y] An array containing the element's x and y coordinates
8490          */
8491         getAnchorXY : function(anchor, local, s){
8492             //Passing a different size is useful for pre-calculating anchors,
8493             //especially for anchored animations that change the el size.
8494
8495             var w, h, vp = false;
8496             if(!s){
8497                 var d = this.dom;
8498                 if(d == document.body || d == document){
8499                     vp = true;
8500                     w = D.getViewWidth(); h = D.getViewHeight();
8501                 }else{
8502                     w = this.getWidth(); h = this.getHeight();
8503                 }
8504             }else{
8505                 w = s.width;  h = s.height;
8506             }
8507             var x = 0, y = 0, r = Math.round;
8508             switch((anchor || "tl").toLowerCase()){
8509                 case "c":
8510                     x = r(w*.5);
8511                     y = r(h*.5);
8512                 break;
8513                 case "t":
8514                     x = r(w*.5);
8515                     y = 0;
8516                 break;
8517                 case "l":
8518                     x = 0;
8519                     y = r(h*.5);
8520                 break;
8521                 case "r":
8522                     x = w;
8523                     y = r(h*.5);
8524                 break;
8525                 case "b":
8526                     x = r(w*.5);
8527                     y = h;
8528                 break;
8529                 case "tl":
8530                     x = 0;
8531                     y = 0;
8532                 break;
8533                 case "bl":
8534                     x = 0;
8535                     y = h;
8536                 break;
8537                 case "br":
8538                     x = w;
8539                     y = h;
8540                 break;
8541                 case "tr":
8542                     x = w;
8543                     y = 0;
8544                 break;
8545             }
8546             if(local === true){
8547                 return [x, y];
8548             }
8549             if(vp){
8550                 var sc = this.getScroll();
8551                 return [x + sc.left, y + sc.top];
8552             }
8553             //Add the element's offset xy
8554             var o = this.getXY();
8555             return [x+o[0], y+o[1]];
8556         },
8557
8558         /**
8559          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8560          * supported position values.
8561          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8562          * @param {String} position The position to align to.
8563          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8564          * @return {Array} [x, y]
8565          */
8566         getAlignToXY : function(el, p, o){
8567             el = Roo.get(el);
8568             var d = this.dom;
8569             if(!el.dom){
8570                 throw "Element.alignTo with an element that doesn't exist";
8571             }
8572             var c = false; //constrain to viewport
8573             var p1 = "", p2 = "";
8574             o = o || [0,0];
8575
8576             if(!p){
8577                 p = "tl-bl";
8578             }else if(p == "?"){
8579                 p = "tl-bl?";
8580             }else if(p.indexOf("-") == -1){
8581                 p = "tl-" + p;
8582             }
8583             p = p.toLowerCase();
8584             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8585             if(!m){
8586                throw "Element.alignTo with an invalid alignment " + p;
8587             }
8588             p1 = m[1]; p2 = m[2]; c = !!m[3];
8589
8590             //Subtract the aligned el's internal xy from the target's offset xy
8591             //plus custom offset to get the aligned el's new offset xy
8592             var a1 = this.getAnchorXY(p1, true);
8593             var a2 = el.getAnchorXY(p2, false);
8594             var x = a2[0] - a1[0] + o[0];
8595             var y = a2[1] - a1[1] + o[1];
8596             if(c){
8597                 //constrain the aligned el to viewport if necessary
8598                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8599                 // 5px of margin for ie
8600                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8601
8602                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8603                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8604                 //otherwise swap the aligned el to the opposite border of the target.
8605                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8606                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8607                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8608                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8609
8610                var doc = document;
8611                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8612                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8613
8614                if((x+w) > dw + scrollX){
8615                     x = swapX ? r.left-w : dw+scrollX-w;
8616                 }
8617                if(x < scrollX){
8618                    x = swapX ? r.right : scrollX;
8619                }
8620                if((y+h) > dh + scrollY){
8621                     y = swapY ? r.top-h : dh+scrollY-h;
8622                 }
8623                if (y < scrollY){
8624                    y = swapY ? r.bottom : scrollY;
8625                }
8626             }
8627             return [x,y];
8628         },
8629
8630         // private
8631         getConstrainToXY : function(){
8632             var os = {top:0, left:0, bottom:0, right: 0};
8633
8634             return function(el, local, offsets, proposedXY){
8635                 el = Roo.get(el);
8636                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8637
8638                 var vw, vh, vx = 0, vy = 0;
8639                 if(el.dom == document.body || el.dom == document){
8640                     vw = Roo.lib.Dom.getViewWidth();
8641                     vh = Roo.lib.Dom.getViewHeight();
8642                 }else{
8643                     vw = el.dom.clientWidth;
8644                     vh = el.dom.clientHeight;
8645                     if(!local){
8646                         var vxy = el.getXY();
8647                         vx = vxy[0];
8648                         vy = vxy[1];
8649                     }
8650                 }
8651
8652                 var s = el.getScroll();
8653
8654                 vx += offsets.left + s.left;
8655                 vy += offsets.top + s.top;
8656
8657                 vw -= offsets.right;
8658                 vh -= offsets.bottom;
8659
8660                 var vr = vx+vw;
8661                 var vb = vy+vh;
8662
8663                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8664                 var x = xy[0], y = xy[1];
8665                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8666
8667                 // only move it if it needs it
8668                 var moved = false;
8669
8670                 // first validate right/bottom
8671                 if((x + w) > vr){
8672                     x = vr - w;
8673                     moved = true;
8674                 }
8675                 if((y + h) > vb){
8676                     y = vb - h;
8677                     moved = true;
8678                 }
8679                 // then make sure top/left isn't negative
8680                 if(x < vx){
8681                     x = vx;
8682                     moved = true;
8683                 }
8684                 if(y < vy){
8685                     y = vy;
8686                     moved = true;
8687                 }
8688                 return moved ? [x, y] : false;
8689             };
8690         }(),
8691
8692         // private
8693         adjustForConstraints : function(xy, parent, offsets){
8694             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8695         },
8696
8697         /**
8698          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8699          * document it aligns it to the viewport.
8700          * The position parameter is optional, and can be specified in any one of the following formats:
8701          * <ul>
8702          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8703          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8704          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8705          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8706          *   <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
8707          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8708          * </ul>
8709          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8710          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8711          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8712          * that specified in order to enforce the viewport constraints.
8713          * Following are all of the supported anchor positions:
8714     <pre>
8715     Value  Description
8716     -----  -----------------------------
8717     tl     The top left corner (default)
8718     t      The center of the top edge
8719     tr     The top right corner
8720     l      The center of the left edge
8721     c      In the center of the element
8722     r      The center of the right edge
8723     bl     The bottom left corner
8724     b      The center of the bottom edge
8725     br     The bottom right corner
8726     </pre>
8727     Example Usage:
8728     <pre><code>
8729     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8730     el.alignTo("other-el");
8731
8732     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8733     el.alignTo("other-el", "tr?");
8734
8735     // align the bottom right corner of el with the center left edge of other-el
8736     el.alignTo("other-el", "br-l?");
8737
8738     // align the center of el with the bottom left corner of other-el and
8739     // adjust the x position by -6 pixels (and the y position by 0)
8740     el.alignTo("other-el", "c-bl", [-6, 0]);
8741     </code></pre>
8742          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8743          * @param {String} position The position to align to.
8744          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8745          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8746          * @return {Roo.Element} this
8747          */
8748         alignTo : function(element, position, offsets, animate){
8749             var xy = this.getAlignToXY(element, position, offsets);
8750             this.setXY(xy, this.preanim(arguments, 3));
8751             return this;
8752         },
8753
8754         /**
8755          * Anchors an element to another element and realigns it when the window is resized.
8756          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8757          * @param {String} position The position to align to.
8758          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8759          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8760          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8761          * is a number, it is used as the buffer delay (defaults to 50ms).
8762          * @param {Function} callback The function to call after the animation finishes
8763          * @return {Roo.Element} this
8764          */
8765         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8766             var action = function(){
8767                 this.alignTo(el, alignment, offsets, animate);
8768                 Roo.callback(callback, this);
8769             };
8770             Roo.EventManager.onWindowResize(action, this);
8771             var tm = typeof monitorScroll;
8772             if(tm != 'undefined'){
8773                 Roo.EventManager.on(window, 'scroll', action, this,
8774                     {buffer: tm == 'number' ? monitorScroll : 50});
8775             }
8776             action.call(this); // align immediately
8777             return this;
8778         },
8779         /**
8780          * Clears any opacity settings from this element. Required in some cases for IE.
8781          * @return {Roo.Element} this
8782          */
8783         clearOpacity : function(){
8784             if (window.ActiveXObject) {
8785                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8786                     this.dom.style.filter = "";
8787                 }
8788             } else {
8789                 this.dom.style.opacity = "";
8790                 this.dom.style["-moz-opacity"] = "";
8791                 this.dom.style["-khtml-opacity"] = "";
8792             }
8793             return this;
8794         },
8795
8796         /**
8797          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8798          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8799          * @return {Roo.Element} this
8800          */
8801         hide : function(animate){
8802             this.setVisible(false, this.preanim(arguments, 0));
8803             return this;
8804         },
8805
8806         /**
8807         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8808         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8809          * @return {Roo.Element} this
8810          */
8811         show : function(animate){
8812             this.setVisible(true, this.preanim(arguments, 0));
8813             return this;
8814         },
8815
8816         /**
8817          * @private Test if size has a unit, otherwise appends the default
8818          */
8819         addUnits : function(size){
8820             return Roo.Element.addUnits(size, this.defaultUnit);
8821         },
8822
8823         /**
8824          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8825          * @return {Roo.Element} this
8826          */
8827         beginMeasure : function(){
8828             var el = this.dom;
8829             if(el.offsetWidth || el.offsetHeight){
8830                 return this; // offsets work already
8831             }
8832             var changed = [];
8833             var p = this.dom, b = document.body; // start with this element
8834             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8835                 var pe = Roo.get(p);
8836                 if(pe.getStyle('display') == 'none'){
8837                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8838                     p.style.visibility = "hidden";
8839                     p.style.display = "block";
8840                 }
8841                 p = p.parentNode;
8842             }
8843             this._measureChanged = changed;
8844             return this;
8845
8846         },
8847
8848         /**
8849          * Restores displays to before beginMeasure was called
8850          * @return {Roo.Element} this
8851          */
8852         endMeasure : function(){
8853             var changed = this._measureChanged;
8854             if(changed){
8855                 for(var i = 0, len = changed.length; i < len; i++) {
8856                     var r = changed[i];
8857                     r.el.style.visibility = r.visibility;
8858                     r.el.style.display = "none";
8859                 }
8860                 this._measureChanged = null;
8861             }
8862             return this;
8863         },
8864
8865         /**
8866         * Update the innerHTML of this element, optionally searching for and processing scripts
8867         * @param {String} html The new HTML
8868         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8869         * @param {Function} callback For async script loading you can be noticed when the update completes
8870         * @return {Roo.Element} this
8871          */
8872         update : function(html, loadScripts, callback){
8873             if(typeof html == "undefined"){
8874                 html = "";
8875             }
8876             if(loadScripts !== true){
8877                 this.dom.innerHTML = html;
8878                 if(typeof callback == "function"){
8879                     callback();
8880                 }
8881                 return this;
8882             }
8883             var id = Roo.id();
8884             var dom = this.dom;
8885
8886             html += '<span id="' + id + '"></span>';
8887
8888             E.onAvailable(id, function(){
8889                 var hd = document.getElementsByTagName("head")[0];
8890                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8891                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8892                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8893
8894                 var match;
8895                 while(match = re.exec(html)){
8896                     var attrs = match[1];
8897                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8898                     if(srcMatch && srcMatch[2]){
8899                        var s = document.createElement("script");
8900                        s.src = srcMatch[2];
8901                        var typeMatch = attrs.match(typeRe);
8902                        if(typeMatch && typeMatch[2]){
8903                            s.type = typeMatch[2];
8904                        }
8905                        hd.appendChild(s);
8906                     }else if(match[2] && match[2].length > 0){
8907                         if(window.execScript) {
8908                            window.execScript(match[2]);
8909                         } else {
8910                             /**
8911                              * eval:var:id
8912                              * eval:var:dom
8913                              * eval:var:html
8914                              * 
8915                              */
8916                            window.eval(match[2]);
8917                         }
8918                     }
8919                 }
8920                 var el = document.getElementById(id);
8921                 if(el){el.parentNode.removeChild(el);}
8922                 if(typeof callback == "function"){
8923                     callback();
8924                 }
8925             });
8926             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8927             return this;
8928         },
8929
8930         /**
8931          * Direct access to the UpdateManager update() method (takes the same parameters).
8932          * @param {String/Function} url The url for this request or a function to call to get the url
8933          * @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}
8934          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8935          * @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.
8936          * @return {Roo.Element} this
8937          */
8938         load : function(){
8939             var um = this.getUpdateManager();
8940             um.update.apply(um, arguments);
8941             return this;
8942         },
8943
8944         /**
8945         * Gets this element's UpdateManager
8946         * @return {Roo.UpdateManager} The UpdateManager
8947         */
8948         getUpdateManager : function(){
8949             if(!this.updateManager){
8950                 this.updateManager = new Roo.UpdateManager(this);
8951             }
8952             return this.updateManager;
8953         },
8954
8955         /**
8956          * Disables text selection for this element (normalized across browsers)
8957          * @return {Roo.Element} this
8958          */
8959         unselectable : function(){
8960             this.dom.unselectable = "on";
8961             this.swallowEvent("selectstart", true);
8962             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8963             this.addClass("x-unselectable");
8964             return this;
8965         },
8966
8967         /**
8968         * Calculates the x, y to center this element on the screen
8969         * @return {Array} The x, y values [x, y]
8970         */
8971         getCenterXY : function(){
8972             return this.getAlignToXY(document, 'c-c');
8973         },
8974
8975         /**
8976         * Centers the Element in either the viewport, or another Element.
8977         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8978         */
8979         center : function(centerIn){
8980             this.alignTo(centerIn || document, 'c-c');
8981             return this;
8982         },
8983
8984         /**
8985          * Tests various css rules/browsers to determine if this element uses a border box
8986          * @return {Boolean}
8987          */
8988         isBorderBox : function(){
8989             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8990         },
8991
8992         /**
8993          * Return a box {x, y, width, height} that can be used to set another elements
8994          * size/location to match this element.
8995          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8996          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8997          * @return {Object} box An object in the format {x, y, width, height}
8998          */
8999         getBox : function(contentBox, local){
9000             var xy;
9001             if(!local){
9002                 xy = this.getXY();
9003             }else{
9004                 var left = parseInt(this.getStyle("left"), 10) || 0;
9005                 var top = parseInt(this.getStyle("top"), 10) || 0;
9006                 xy = [left, top];
9007             }
9008             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9009             if(!contentBox){
9010                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9011             }else{
9012                 var l = this.getBorderWidth("l")+this.getPadding("l");
9013                 var r = this.getBorderWidth("r")+this.getPadding("r");
9014                 var t = this.getBorderWidth("t")+this.getPadding("t");
9015                 var b = this.getBorderWidth("b")+this.getPadding("b");
9016                 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)};
9017             }
9018             bx.right = bx.x + bx.width;
9019             bx.bottom = bx.y + bx.height;
9020             return bx;
9021         },
9022
9023         /**
9024          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9025          for more information about the sides.
9026          * @param {String} sides
9027          * @return {Number}
9028          */
9029         getFrameWidth : function(sides, onlyContentBox){
9030             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9031         },
9032
9033         /**
9034          * 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.
9035          * @param {Object} box The box to fill {x, y, width, height}
9036          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9037          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9038          * @return {Roo.Element} this
9039          */
9040         setBox : function(box, adjust, animate){
9041             var w = box.width, h = box.height;
9042             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9043                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9044                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9045             }
9046             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9047             return this;
9048         },
9049
9050         /**
9051          * Forces the browser to repaint this element
9052          * @return {Roo.Element} this
9053          */
9054          repaint : function(){
9055             var dom = this.dom;
9056             this.addClass("x-repaint");
9057             setTimeout(function(){
9058                 Roo.get(dom).removeClass("x-repaint");
9059             }, 1);
9060             return this;
9061         },
9062
9063         /**
9064          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9065          * then it returns the calculated width of the sides (see getPadding)
9066          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9067          * @return {Object/Number}
9068          */
9069         getMargins : function(side){
9070             if(!side){
9071                 return {
9072                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9073                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9074                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9075                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9076                 };
9077             }else{
9078                 return this.addStyles(side, El.margins);
9079              }
9080         },
9081
9082         // private
9083         addStyles : function(sides, styles){
9084             var val = 0, v, w;
9085             for(var i = 0, len = sides.length; i < len; i++){
9086                 v = this.getStyle(styles[sides.charAt(i)]);
9087                 if(v){
9088                      w = parseInt(v, 10);
9089                      if(w){ val += w; }
9090                 }
9091             }
9092             return val;
9093         },
9094
9095         /**
9096          * Creates a proxy element of this element
9097          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9098          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9099          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9100          * @return {Roo.Element} The new proxy element
9101          */
9102         createProxy : function(config, renderTo, matchBox){
9103             if(renderTo){
9104                 renderTo = Roo.getDom(renderTo);
9105             }else{
9106                 renderTo = document.body;
9107             }
9108             config = typeof config == "object" ?
9109                 config : {tag : "div", cls: config};
9110             var proxy = Roo.DomHelper.append(renderTo, config, true);
9111             if(matchBox){
9112                proxy.setBox(this.getBox());
9113             }
9114             return proxy;
9115         },
9116
9117         /**
9118          * Puts a mask over this element to disable user interaction. Requires core.css.
9119          * This method can only be applied to elements which accept child nodes.
9120          * @param {String} msg (optional) A message to display in the mask
9121          * @param {String} msgCls (optional) A css class to apply to the msg element
9122          * @return {Element} The mask  element
9123          */
9124         mask : function(msg, msgCls)
9125         {
9126             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9127                 this.setStyle("position", "relative");
9128             }
9129             if(!this._mask){
9130                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9131             }
9132             
9133             this._mask.dom.className = msgCls ? "roo-el-mask " + msgCls : "roo-el-mask";
9134             
9135             this.addClass("x-masked");
9136             this._mask.setDisplayed(true);
9137             
9138             // we wander
9139             var z = 0;
9140             var dom = this.dom;
9141             while (dom && dom.style) {
9142                 if (!isNaN(parseInt(dom.style.zIndex))) {
9143                     z = Math.max(z, parseInt(dom.style.zIndex));
9144                 }
9145                 dom = dom.parentNode;
9146             }
9147             // if we are masking the body - then it hides everything..
9148             if (this.dom == document.body) {
9149                 z = 1000000;
9150                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9151                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9152             }
9153            
9154             if(typeof msg == 'string'){
9155                 if(!this._maskMsg){
9156                     this._maskMsg = Roo.DomHelper.append(this.dom, {
9157                         cls: "roo-el-mask-msg", 
9158                         cn: [
9159                             {
9160                                 tag: 'i',
9161                                 cls: 'fa fa-spinner fa-spin'
9162                             },
9163                             {
9164                                 tag: 'div'
9165                             }   
9166                         ]
9167                     }, true);
9168                 }
9169                 var mm = this._maskMsg;
9170                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9171                 if (mm.dom.lastChild) { // weird IE issue?
9172                     mm.dom.lastChild.innerHTML = msg;
9173                 }
9174                 mm.setDisplayed(true);
9175                 mm.center(this);
9176                 mm.setStyle('z-index', z + 102);
9177             }
9178             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9179                 this._mask.setHeight(this.getHeight());
9180             }
9181             this._mask.setStyle('z-index', z + 100);
9182             
9183             return this._mask;
9184         },
9185
9186         /**
9187          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9188          * it is cached for reuse.
9189          */
9190         unmask : function(removeEl){
9191             if(this._mask){
9192                 if(removeEl === true){
9193                     this._mask.remove();
9194                     delete this._mask;
9195                     if(this._maskMsg){
9196                         this._maskMsg.remove();
9197                         delete this._maskMsg;
9198                     }
9199                 }else{
9200                     this._mask.setDisplayed(false);
9201                     if(this._maskMsg){
9202                         this._maskMsg.setDisplayed(false);
9203                     }
9204                 }
9205             }
9206             this.removeClass("x-masked");
9207         },
9208
9209         /**
9210          * Returns true if this element is masked
9211          * @return {Boolean}
9212          */
9213         isMasked : function(){
9214             return this._mask && this._mask.isVisible();
9215         },
9216
9217         /**
9218          * Creates an iframe shim for this element to keep selects and other windowed objects from
9219          * showing through.
9220          * @return {Roo.Element} The new shim element
9221          */
9222         createShim : function(){
9223             var el = document.createElement('iframe');
9224             el.frameBorder = 'no';
9225             el.className = 'roo-shim';
9226             if(Roo.isIE && Roo.isSecure){
9227                 el.src = Roo.SSL_SECURE_URL;
9228             }
9229             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9230             shim.autoBoxAdjust = false;
9231             return shim;
9232         },
9233
9234         /**
9235          * Removes this element from the DOM and deletes it from the cache
9236          */
9237         remove : function(){
9238             if(this.dom.parentNode){
9239                 this.dom.parentNode.removeChild(this.dom);
9240             }
9241             delete El.cache[this.dom.id];
9242         },
9243
9244         /**
9245          * Sets up event handlers to add and remove a css class when the mouse is over this element
9246          * @param {String} className
9247          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9248          * mouseout events for children elements
9249          * @return {Roo.Element} this
9250          */
9251         addClassOnOver : function(className, preventFlicker){
9252             this.on("mouseover", function(){
9253                 Roo.fly(this, '_internal').addClass(className);
9254             }, this.dom);
9255             var removeFn = function(e){
9256                 if(preventFlicker !== true || !e.within(this, true)){
9257                     Roo.fly(this, '_internal').removeClass(className);
9258                 }
9259             };
9260             this.on("mouseout", removeFn, this.dom);
9261             return this;
9262         },
9263
9264         /**
9265          * Sets up event handlers to add and remove a css class when this element has the focus
9266          * @param {String} className
9267          * @return {Roo.Element} this
9268          */
9269         addClassOnFocus : function(className){
9270             this.on("focus", function(){
9271                 Roo.fly(this, '_internal').addClass(className);
9272             }, this.dom);
9273             this.on("blur", function(){
9274                 Roo.fly(this, '_internal').removeClass(className);
9275             }, this.dom);
9276             return this;
9277         },
9278         /**
9279          * 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)
9280          * @param {String} className
9281          * @return {Roo.Element} this
9282          */
9283         addClassOnClick : function(className){
9284             var dom = this.dom;
9285             this.on("mousedown", function(){
9286                 Roo.fly(dom, '_internal').addClass(className);
9287                 var d = Roo.get(document);
9288                 var fn = function(){
9289                     Roo.fly(dom, '_internal').removeClass(className);
9290                     d.removeListener("mouseup", fn);
9291                 };
9292                 d.on("mouseup", fn);
9293             });
9294             return this;
9295         },
9296
9297         /**
9298          * Stops the specified event from bubbling and optionally prevents the default action
9299          * @param {String} eventName
9300          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9301          * @return {Roo.Element} this
9302          */
9303         swallowEvent : function(eventName, preventDefault){
9304             var fn = function(e){
9305                 e.stopPropagation();
9306                 if(preventDefault){
9307                     e.preventDefault();
9308                 }
9309             };
9310             if(eventName instanceof Array){
9311                 for(var i = 0, len = eventName.length; i < len; i++){
9312                      this.on(eventName[i], fn);
9313                 }
9314                 return this;
9315             }
9316             this.on(eventName, fn);
9317             return this;
9318         },
9319
9320         /**
9321          * @private
9322          */
9323       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9324
9325         /**
9326          * Sizes this element to its parent element's dimensions performing
9327          * neccessary box adjustments.
9328          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9329          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9330          * @return {Roo.Element} this
9331          */
9332         fitToParent : function(monitorResize, targetParent) {
9333           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9334           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9335           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9336             return;
9337           }
9338           var p = Roo.get(targetParent || this.dom.parentNode);
9339           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9340           if (monitorResize === true) {
9341             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9342             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9343           }
9344           return this;
9345         },
9346
9347         /**
9348          * Gets the next sibling, skipping text nodes
9349          * @return {HTMLElement} The next sibling or null
9350          */
9351         getNextSibling : function(){
9352             var n = this.dom.nextSibling;
9353             while(n && n.nodeType != 1){
9354                 n = n.nextSibling;
9355             }
9356             return n;
9357         },
9358
9359         /**
9360          * Gets the previous sibling, skipping text nodes
9361          * @return {HTMLElement} The previous sibling or null
9362          */
9363         getPrevSibling : function(){
9364             var n = this.dom.previousSibling;
9365             while(n && n.nodeType != 1){
9366                 n = n.previousSibling;
9367             }
9368             return n;
9369         },
9370
9371
9372         /**
9373          * Appends the passed element(s) to this element
9374          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9375          * @return {Roo.Element} this
9376          */
9377         appendChild: function(el){
9378             el = Roo.get(el);
9379             el.appendTo(this);
9380             return this;
9381         },
9382
9383         /**
9384          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9385          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9386          * automatically generated with the specified attributes.
9387          * @param {HTMLElement} insertBefore (optional) a child element of this element
9388          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9389          * @return {Roo.Element} The new child element
9390          */
9391         createChild: function(config, insertBefore, returnDom){
9392             config = config || {tag:'div'};
9393             if(insertBefore){
9394                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9395             }
9396             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9397         },
9398
9399         /**
9400          * Appends this element to the passed element
9401          * @param {String/HTMLElement/Element} el The new parent element
9402          * @return {Roo.Element} this
9403          */
9404         appendTo: function(el){
9405             el = Roo.getDom(el);
9406             el.appendChild(this.dom);
9407             return this;
9408         },
9409
9410         /**
9411          * Inserts this element before the passed element in the DOM
9412          * @param {String/HTMLElement/Element} el The element to insert before
9413          * @return {Roo.Element} this
9414          */
9415         insertBefore: function(el){
9416             el = Roo.getDom(el);
9417             el.parentNode.insertBefore(this.dom, el);
9418             return this;
9419         },
9420
9421         /**
9422          * Inserts this element after the passed element in the DOM
9423          * @param {String/HTMLElement/Element} el The element to insert after
9424          * @return {Roo.Element} this
9425          */
9426         insertAfter: function(el){
9427             el = Roo.getDom(el);
9428             el.parentNode.insertBefore(this.dom, el.nextSibling);
9429             return this;
9430         },
9431
9432         /**
9433          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9434          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9435          * @return {Roo.Element} The new child
9436          */
9437         insertFirst: function(el, returnDom){
9438             el = el || {};
9439             if(typeof el == 'object' && !el.nodeType){ // dh config
9440                 return this.createChild(el, this.dom.firstChild, returnDom);
9441             }else{
9442                 el = Roo.getDom(el);
9443                 this.dom.insertBefore(el, this.dom.firstChild);
9444                 return !returnDom ? Roo.get(el) : el;
9445             }
9446         },
9447
9448         /**
9449          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9450          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9451          * @param {String} where (optional) 'before' or 'after' defaults to before
9452          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9453          * @return {Roo.Element} the inserted Element
9454          */
9455         insertSibling: function(el, where, returnDom){
9456             where = where ? where.toLowerCase() : 'before';
9457             el = el || {};
9458             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9459
9460             if(typeof el == 'object' && !el.nodeType){ // dh config
9461                 if(where == 'after' && !this.dom.nextSibling){
9462                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9463                 }else{
9464                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9465                 }
9466
9467             }else{
9468                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9469                             where == 'before' ? this.dom : this.dom.nextSibling);
9470                 if(!returnDom){
9471                     rt = Roo.get(rt);
9472                 }
9473             }
9474             return rt;
9475         },
9476
9477         /**
9478          * Creates and wraps this element with another element
9479          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9480          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9481          * @return {HTMLElement/Element} The newly created wrapper element
9482          */
9483         wrap: function(config, returnDom){
9484             if(!config){
9485                 config = {tag: "div"};
9486             }
9487             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9488             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9489             return newEl;
9490         },
9491
9492         /**
9493          * Replaces the passed element with this element
9494          * @param {String/HTMLElement/Element} el The element to replace
9495          * @return {Roo.Element} this
9496          */
9497         replace: function(el){
9498             el = Roo.get(el);
9499             this.insertBefore(el);
9500             el.remove();
9501             return this;
9502         },
9503
9504         /**
9505          * Inserts an html fragment into this element
9506          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9507          * @param {String} html The HTML fragment
9508          * @param {Boolean} returnEl True to return an Roo.Element
9509          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9510          */
9511         insertHtml : function(where, html, returnEl){
9512             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9513             return returnEl ? Roo.get(el) : el;
9514         },
9515
9516         /**
9517          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9518          * @param {Object} o The object with the attributes
9519          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9520          * @return {Roo.Element} this
9521          */
9522         set : function(o, useSet){
9523             var el = this.dom;
9524             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9525             for(var attr in o){
9526                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9527                 if(attr=="cls"){
9528                     el.className = o["cls"];
9529                 }else{
9530                     if(useSet) {
9531                         el.setAttribute(attr, o[attr]);
9532                     } else {
9533                         el[attr] = o[attr];
9534                     }
9535                 }
9536             }
9537             if(o.style){
9538                 Roo.DomHelper.applyStyles(el, o.style);
9539             }
9540             return this;
9541         },
9542
9543         /**
9544          * Convenience method for constructing a KeyMap
9545          * @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:
9546          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9547          * @param {Function} fn The function to call
9548          * @param {Object} scope (optional) The scope of the function
9549          * @return {Roo.KeyMap} The KeyMap created
9550          */
9551         addKeyListener : function(key, fn, scope){
9552             var config;
9553             if(typeof key != "object" || key instanceof Array){
9554                 config = {
9555                     key: key,
9556                     fn: fn,
9557                     scope: scope
9558                 };
9559             }else{
9560                 config = {
9561                     key : key.key,
9562                     shift : key.shift,
9563                     ctrl : key.ctrl,
9564                     alt : key.alt,
9565                     fn: fn,
9566                     scope: scope
9567                 };
9568             }
9569             return new Roo.KeyMap(this, config);
9570         },
9571
9572         /**
9573          * Creates a KeyMap for this element
9574          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9575          * @return {Roo.KeyMap} The KeyMap created
9576          */
9577         addKeyMap : function(config){
9578             return new Roo.KeyMap(this, config);
9579         },
9580
9581         /**
9582          * Returns true if this element is scrollable.
9583          * @return {Boolean}
9584          */
9585          isScrollable : function(){
9586             var dom = this.dom;
9587             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9588         },
9589
9590         /**
9591          * 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().
9592          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9593          * @param {Number} value The new scroll value
9594          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9595          * @return {Element} this
9596          */
9597
9598         scrollTo : function(side, value, animate){
9599             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9600             if(!animate || !A){
9601                 this.dom[prop] = value;
9602             }else{
9603                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9604                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9605             }
9606             return this;
9607         },
9608
9609         /**
9610          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9611          * within this element's scrollable range.
9612          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9613          * @param {Number} distance How far to scroll the element in pixels
9614          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9615          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9616          * was scrolled as far as it could go.
9617          */
9618          scroll : function(direction, distance, animate){
9619              if(!this.isScrollable()){
9620                  return;
9621              }
9622              var el = this.dom;
9623              var l = el.scrollLeft, t = el.scrollTop;
9624              var w = el.scrollWidth, h = el.scrollHeight;
9625              var cw = el.clientWidth, ch = el.clientHeight;
9626              direction = direction.toLowerCase();
9627              var scrolled = false;
9628              var a = this.preanim(arguments, 2);
9629              switch(direction){
9630                  case "l":
9631                  case "left":
9632                      if(w - l > cw){
9633                          var v = Math.min(l + distance, w-cw);
9634                          this.scrollTo("left", v, a);
9635                          scrolled = true;
9636                      }
9637                      break;
9638                 case "r":
9639                 case "right":
9640                      if(l > 0){
9641                          var v = Math.max(l - distance, 0);
9642                          this.scrollTo("left", v, a);
9643                          scrolled = true;
9644                      }
9645                      break;
9646                 case "t":
9647                 case "top":
9648                 case "up":
9649                      if(t > 0){
9650                          var v = Math.max(t - distance, 0);
9651                          this.scrollTo("top", v, a);
9652                          scrolled = true;
9653                      }
9654                      break;
9655                 case "b":
9656                 case "bottom":
9657                 case "down":
9658                      if(h - t > ch){
9659                          var v = Math.min(t + distance, h-ch);
9660                          this.scrollTo("top", v, a);
9661                          scrolled = true;
9662                      }
9663                      break;
9664              }
9665              return scrolled;
9666         },
9667
9668         /**
9669          * Translates the passed page coordinates into left/top css values for this element
9670          * @param {Number/Array} x The page x or an array containing [x, y]
9671          * @param {Number} y The page y
9672          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9673          */
9674         translatePoints : function(x, y){
9675             if(typeof x == 'object' || x instanceof Array){
9676                 y = x[1]; x = x[0];
9677             }
9678             var p = this.getStyle('position');
9679             var o = this.getXY();
9680
9681             var l = parseInt(this.getStyle('left'), 10);
9682             var t = parseInt(this.getStyle('top'), 10);
9683
9684             if(isNaN(l)){
9685                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9686             }
9687             if(isNaN(t)){
9688                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9689             }
9690
9691             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9692         },
9693
9694         /**
9695          * Returns the current scroll position of the element.
9696          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9697          */
9698         getScroll : function(){
9699             var d = this.dom, doc = document;
9700             if(d == doc || d == doc.body){
9701                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9702                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9703                 return {left: l, top: t};
9704             }else{
9705                 return {left: d.scrollLeft, top: d.scrollTop};
9706             }
9707         },
9708
9709         /**
9710          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9711          * are convert to standard 6 digit hex color.
9712          * @param {String} attr The css attribute
9713          * @param {String} defaultValue The default value to use when a valid color isn't found
9714          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9715          * YUI color anims.
9716          */
9717         getColor : function(attr, defaultValue, prefix){
9718             var v = this.getStyle(attr);
9719             if(!v || v == "transparent" || v == "inherit") {
9720                 return defaultValue;
9721             }
9722             var color = typeof prefix == "undefined" ? "#" : prefix;
9723             if(v.substr(0, 4) == "rgb("){
9724                 var rvs = v.slice(4, v.length -1).split(",");
9725                 for(var i = 0; i < 3; i++){
9726                     var h = parseInt(rvs[i]).toString(16);
9727                     if(h < 16){
9728                         h = "0" + h;
9729                     }
9730                     color += h;
9731                 }
9732             } else {
9733                 if(v.substr(0, 1) == "#"){
9734                     if(v.length == 4) {
9735                         for(var i = 1; i < 4; i++){
9736                             var c = v.charAt(i);
9737                             color +=  c + c;
9738                         }
9739                     }else if(v.length == 7){
9740                         color += v.substr(1);
9741                     }
9742                 }
9743             }
9744             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9745         },
9746
9747         /**
9748          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9749          * gradient background, rounded corners and a 4-way shadow.
9750          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9751          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9752          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9753          * @return {Roo.Element} this
9754          */
9755         boxWrap : function(cls){
9756             cls = cls || 'x-box';
9757             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9758             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9759             return el;
9760         },
9761
9762         /**
9763          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9764          * @param {String} namespace The namespace in which to look for the attribute
9765          * @param {String} name The attribute name
9766          * @return {String} The attribute value
9767          */
9768         getAttributeNS : Roo.isIE ? function(ns, name){
9769             var d = this.dom;
9770             var type = typeof d[ns+":"+name];
9771             if(type != 'undefined' && type != 'unknown'){
9772                 return d[ns+":"+name];
9773             }
9774             return d[name];
9775         } : function(ns, name){
9776             var d = this.dom;
9777             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9778         },
9779         
9780         
9781         /**
9782          * Sets or Returns the value the dom attribute value
9783          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9784          * @param {String} value (optional) The value to set the attribute to
9785          * @return {String} The attribute value
9786          */
9787         attr : function(name){
9788             if (arguments.length > 1) {
9789                 this.dom.setAttribute(name, arguments[1]);
9790                 return arguments[1];
9791             }
9792             if (typeof(name) == 'object') {
9793                 for(var i in name) {
9794                     this.attr(i, name[i]);
9795                 }
9796                 return name;
9797             }
9798             
9799             
9800             if (!this.dom.hasAttribute(name)) {
9801                 return undefined;
9802             }
9803             return this.dom.getAttribute(name);
9804         }
9805         
9806         
9807         
9808     };
9809
9810     var ep = El.prototype;
9811
9812     /**
9813      * Appends an event handler (Shorthand for addListener)
9814      * @param {String}   eventName     The type of event to append
9815      * @param {Function} fn        The method the event invokes
9816      * @param {Object} scope       (optional) The scope (this object) of the fn
9817      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9818      * @method
9819      */
9820     ep.on = ep.addListener;
9821         // backwards compat
9822     ep.mon = ep.addListener;
9823
9824     /**
9825      * Removes an event handler from this element (shorthand for removeListener)
9826      * @param {String} eventName the type of event to remove
9827      * @param {Function} fn the method the event invokes
9828      * @return {Roo.Element} this
9829      * @method
9830      */
9831     ep.un = ep.removeListener;
9832
9833     /**
9834      * true to automatically adjust width and height settings for box-model issues (default to true)
9835      */
9836     ep.autoBoxAdjust = true;
9837
9838     // private
9839     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9840
9841     // private
9842     El.addUnits = function(v, defaultUnit){
9843         if(v === "" || v == "auto"){
9844             return v;
9845         }
9846         if(v === undefined){
9847             return '';
9848         }
9849         if(typeof v == "number" || !El.unitPattern.test(v)){
9850             return v + (defaultUnit || 'px');
9851         }
9852         return v;
9853     };
9854
9855     // special markup used throughout Roo when box wrapping elements
9856     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>';
9857     /**
9858      * Visibility mode constant - Use visibility to hide element
9859      * @static
9860      * @type Number
9861      */
9862     El.VISIBILITY = 1;
9863     /**
9864      * Visibility mode constant - Use display to hide element
9865      * @static
9866      * @type Number
9867      */
9868     El.DISPLAY = 2;
9869
9870     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9871     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9872     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9873
9874
9875
9876     /**
9877      * @private
9878      */
9879     El.cache = {};
9880
9881     var docEl;
9882
9883     /**
9884      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9885      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9886      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9887      * @return {Element} The Element object
9888      * @static
9889      */
9890     El.get = function(el){
9891         var ex, elm, id;
9892         if(!el){ return null; }
9893         if(typeof el == "string"){ // element id
9894             if(!(elm = document.getElementById(el))){
9895                 return null;
9896             }
9897             if(ex = El.cache[el]){
9898                 ex.dom = elm;
9899             }else{
9900                 ex = El.cache[el] = new El(elm);
9901             }
9902             return ex;
9903         }else if(el.tagName){ // dom element
9904             if(!(id = el.id)){
9905                 id = Roo.id(el);
9906             }
9907             if(ex = El.cache[id]){
9908                 ex.dom = el;
9909             }else{
9910                 ex = El.cache[id] = new El(el);
9911             }
9912             return ex;
9913         }else if(el instanceof El){
9914             if(el != docEl){
9915                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9916                                                               // catch case where it hasn't been appended
9917                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9918             }
9919             return el;
9920         }else if(el.isComposite){
9921             return el;
9922         }else if(el instanceof Array){
9923             return El.select(el);
9924         }else if(el == document){
9925             // create a bogus element object representing the document object
9926             if(!docEl){
9927                 var f = function(){};
9928                 f.prototype = El.prototype;
9929                 docEl = new f();
9930                 docEl.dom = document;
9931             }
9932             return docEl;
9933         }
9934         return null;
9935     };
9936
9937     // private
9938     El.uncache = function(el){
9939         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9940             if(a[i]){
9941                 delete El.cache[a[i].id || a[i]];
9942             }
9943         }
9944     };
9945
9946     // private
9947     // Garbage collection - uncache elements/purge listeners on orphaned elements
9948     // so we don't hold a reference and cause the browser to retain them
9949     El.garbageCollect = function(){
9950         if(!Roo.enableGarbageCollector){
9951             clearInterval(El.collectorThread);
9952             return;
9953         }
9954         for(var eid in El.cache){
9955             var el = El.cache[eid], d = el.dom;
9956             // -------------------------------------------------------
9957             // Determining what is garbage:
9958             // -------------------------------------------------------
9959             // !d
9960             // dom node is null, definitely garbage
9961             // -------------------------------------------------------
9962             // !d.parentNode
9963             // no parentNode == direct orphan, definitely garbage
9964             // -------------------------------------------------------
9965             // !d.offsetParent && !document.getElementById(eid)
9966             // display none elements have no offsetParent so we will
9967             // also try to look it up by it's id. However, check
9968             // offsetParent first so we don't do unneeded lookups.
9969             // This enables collection of elements that are not orphans
9970             // directly, but somewhere up the line they have an orphan
9971             // parent.
9972             // -------------------------------------------------------
9973             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9974                 delete El.cache[eid];
9975                 if(d && Roo.enableListenerCollection){
9976                     E.purgeElement(d);
9977                 }
9978             }
9979         }
9980     }
9981     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9982
9983
9984     // dom is optional
9985     El.Flyweight = function(dom){
9986         this.dom = dom;
9987     };
9988     El.Flyweight.prototype = El.prototype;
9989
9990     El._flyweights = {};
9991     /**
9992      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9993      * the dom node can be overwritten by other code.
9994      * @param {String/HTMLElement} el The dom node or id
9995      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9996      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9997      * @static
9998      * @return {Element} The shared Element object
9999      */
10000     El.fly = function(el, named){
10001         named = named || '_global';
10002         el = Roo.getDom(el);
10003         if(!el){
10004             return null;
10005         }
10006         if(!El._flyweights[named]){
10007             El._flyweights[named] = new El.Flyweight();
10008         }
10009         El._flyweights[named].dom = el;
10010         return El._flyweights[named];
10011     };
10012
10013     /**
10014      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10015      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10016      * Shorthand of {@link Roo.Element#get}
10017      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10018      * @return {Element} The Element object
10019      * @member Roo
10020      * @method get
10021      */
10022     Roo.get = El.get;
10023     /**
10024      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10025      * the dom node can be overwritten by other code.
10026      * Shorthand of {@link Roo.Element#fly}
10027      * @param {String/HTMLElement} el The dom node or id
10028      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10029      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10030      * @static
10031      * @return {Element} The shared Element object
10032      * @member Roo
10033      * @method fly
10034      */
10035     Roo.fly = El.fly;
10036
10037     // speedy lookup for elements never to box adjust
10038     var noBoxAdjust = Roo.isStrict ? {
10039         select:1
10040     } : {
10041         input:1, select:1, textarea:1
10042     };
10043     if(Roo.isIE || Roo.isGecko){
10044         noBoxAdjust['button'] = 1;
10045     }
10046
10047
10048     Roo.EventManager.on(window, 'unload', function(){
10049         delete El.cache;
10050         delete El._flyweights;
10051     });
10052 })();
10053
10054
10055
10056
10057 if(Roo.DomQuery){
10058     Roo.Element.selectorFunction = Roo.DomQuery.select;
10059 }
10060
10061 Roo.Element.select = function(selector, unique, root){
10062     var els;
10063     if(typeof selector == "string"){
10064         els = Roo.Element.selectorFunction(selector, root);
10065     }else if(selector.length !== undefined){
10066         els = selector;
10067     }else{
10068         throw "Invalid selector";
10069     }
10070     if(unique === true){
10071         return new Roo.CompositeElement(els);
10072     }else{
10073         return new Roo.CompositeElementLite(els);
10074     }
10075 };
10076 /**
10077  * Selects elements based on the passed CSS selector to enable working on them as 1.
10078  * @param {String/Array} selector The CSS selector or an array of elements
10079  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10080  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10081  * @return {CompositeElementLite/CompositeElement}
10082  * @member Roo
10083  * @method select
10084  */
10085 Roo.select = Roo.Element.select;
10086
10087
10088
10089
10090
10091
10092
10093
10094
10095
10096
10097
10098
10099
10100 /*
10101  * Based on:
10102  * Ext JS Library 1.1.1
10103  * Copyright(c) 2006-2007, Ext JS, LLC.
10104  *
10105  * Originally Released Under LGPL - original licence link has changed is not relivant.
10106  *
10107  * Fork - LGPL
10108  * <script type="text/javascript">
10109  */
10110
10111
10112
10113 //Notifies Element that fx methods are available
10114 Roo.enableFx = true;
10115
10116 /**
10117  * @class Roo.Fx
10118  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10119  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10120  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10121  * Element effects to work.</p><br/>
10122  *
10123  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10124  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10125  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10126  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10127  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10128  * expected results and should be done with care.</p><br/>
10129  *
10130  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10131  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10132 <pre>
10133 Value  Description
10134 -----  -----------------------------
10135 tl     The top left corner
10136 t      The center of the top edge
10137 tr     The top right corner
10138 l      The center of the left edge
10139 r      The center of the right edge
10140 bl     The bottom left corner
10141 b      The center of the bottom edge
10142 br     The bottom right corner
10143 </pre>
10144  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10145  * below are common options that can be passed to any Fx method.</b>
10146  * @cfg {Function} callback A function called when the effect is finished
10147  * @cfg {Object} scope The scope of the effect function
10148  * @cfg {String} easing A valid Easing value for the effect
10149  * @cfg {String} afterCls A css class to apply after the effect
10150  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10151  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10152  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10153  * effects that end with the element being visually hidden, ignored otherwise)
10154  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10155  * a function which returns such a specification that will be applied to the Element after the effect finishes
10156  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10157  * @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
10158  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10159  */
10160 Roo.Fx = {
10161         /**
10162          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10163          * origin for the slide effect.  This function automatically handles wrapping the element with
10164          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10165          * Usage:
10166          *<pre><code>
10167 // default: slide the element in from the top
10168 el.slideIn();
10169
10170 // custom: slide the element in from the right with a 2-second duration
10171 el.slideIn('r', { duration: 2 });
10172
10173 // common config options shown with default values
10174 el.slideIn('t', {
10175     easing: 'easeOut',
10176     duration: .5
10177 });
10178 </code></pre>
10179          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10180          * @param {Object} options (optional) Object literal with any of the Fx config options
10181          * @return {Roo.Element} The Element
10182          */
10183     slideIn : function(anchor, o){
10184         var el = this.getFxEl();
10185         o = o || {};
10186
10187         el.queueFx(o, function(){
10188
10189             anchor = anchor || "t";
10190
10191             // fix display to visibility
10192             this.fixDisplay();
10193
10194             // restore values after effect
10195             var r = this.getFxRestore();
10196             var b = this.getBox();
10197             // fixed size for slide
10198             this.setSize(b);
10199
10200             // wrap if needed
10201             var wrap = this.fxWrap(r.pos, o, "hidden");
10202
10203             var st = this.dom.style;
10204             st.visibility = "visible";
10205             st.position = "absolute";
10206
10207             // clear out temp styles after slide and unwrap
10208             var after = function(){
10209                 el.fxUnwrap(wrap, r.pos, o);
10210                 st.width = r.width;
10211                 st.height = r.height;
10212                 el.afterFx(o);
10213             };
10214             // time to calc the positions
10215             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10216
10217             switch(anchor.toLowerCase()){
10218                 case "t":
10219                     wrap.setSize(b.width, 0);
10220                     st.left = st.bottom = "0";
10221                     a = {height: bh};
10222                 break;
10223                 case "l":
10224                     wrap.setSize(0, b.height);
10225                     st.right = st.top = "0";
10226                     a = {width: bw};
10227                 break;
10228                 case "r":
10229                     wrap.setSize(0, b.height);
10230                     wrap.setX(b.right);
10231                     st.left = st.top = "0";
10232                     a = {width: bw, points: pt};
10233                 break;
10234                 case "b":
10235                     wrap.setSize(b.width, 0);
10236                     wrap.setY(b.bottom);
10237                     st.left = st.top = "0";
10238                     a = {height: bh, points: pt};
10239                 break;
10240                 case "tl":
10241                     wrap.setSize(0, 0);
10242                     st.right = st.bottom = "0";
10243                     a = {width: bw, height: bh};
10244                 break;
10245                 case "bl":
10246                     wrap.setSize(0, 0);
10247                     wrap.setY(b.y+b.height);
10248                     st.right = st.top = "0";
10249                     a = {width: bw, height: bh, points: pt};
10250                 break;
10251                 case "br":
10252                     wrap.setSize(0, 0);
10253                     wrap.setXY([b.right, b.bottom]);
10254                     st.left = st.top = "0";
10255                     a = {width: bw, height: bh, points: pt};
10256                 break;
10257                 case "tr":
10258                     wrap.setSize(0, 0);
10259                     wrap.setX(b.x+b.width);
10260                     st.left = st.bottom = "0";
10261                     a = {width: bw, height: bh, points: pt};
10262                 break;
10263             }
10264             this.dom.style.visibility = "visible";
10265             wrap.show();
10266
10267             arguments.callee.anim = wrap.fxanim(a,
10268                 o,
10269                 'motion',
10270                 .5,
10271                 'easeOut', after);
10272         });
10273         return this;
10274     },
10275     
10276         /**
10277          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10278          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10279          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10280          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10281          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10282          * Usage:
10283          *<pre><code>
10284 // default: slide the element out to the top
10285 el.slideOut();
10286
10287 // custom: slide the element out to the right with a 2-second duration
10288 el.slideOut('r', { duration: 2 });
10289
10290 // common config options shown with default values
10291 el.slideOut('t', {
10292     easing: 'easeOut',
10293     duration: .5,
10294     remove: false,
10295     useDisplay: false
10296 });
10297 </code></pre>
10298          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10299          * @param {Object} options (optional) Object literal with any of the Fx config options
10300          * @return {Roo.Element} The Element
10301          */
10302     slideOut : function(anchor, o){
10303         var el = this.getFxEl();
10304         o = o || {};
10305
10306         el.queueFx(o, function(){
10307
10308             anchor = anchor || "t";
10309
10310             // restore values after effect
10311             var r = this.getFxRestore();
10312             
10313             var b = this.getBox();
10314             // fixed size for slide
10315             this.setSize(b);
10316
10317             // wrap if needed
10318             var wrap = this.fxWrap(r.pos, o, "visible");
10319
10320             var st = this.dom.style;
10321             st.visibility = "visible";
10322             st.position = "absolute";
10323
10324             wrap.setSize(b);
10325
10326             var after = function(){
10327                 if(o.useDisplay){
10328                     el.setDisplayed(false);
10329                 }else{
10330                     el.hide();
10331                 }
10332
10333                 el.fxUnwrap(wrap, r.pos, o);
10334
10335                 st.width = r.width;
10336                 st.height = r.height;
10337
10338                 el.afterFx(o);
10339             };
10340
10341             var a, zero = {to: 0};
10342             switch(anchor.toLowerCase()){
10343                 case "t":
10344                     st.left = st.bottom = "0";
10345                     a = {height: zero};
10346                 break;
10347                 case "l":
10348                     st.right = st.top = "0";
10349                     a = {width: zero};
10350                 break;
10351                 case "r":
10352                     st.left = st.top = "0";
10353                     a = {width: zero, points: {to:[b.right, b.y]}};
10354                 break;
10355                 case "b":
10356                     st.left = st.top = "0";
10357                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10358                 break;
10359                 case "tl":
10360                     st.right = st.bottom = "0";
10361                     a = {width: zero, height: zero};
10362                 break;
10363                 case "bl":
10364                     st.right = st.top = "0";
10365                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10366                 break;
10367                 case "br":
10368                     st.left = st.top = "0";
10369                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10370                 break;
10371                 case "tr":
10372                     st.left = st.bottom = "0";
10373                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10374                 break;
10375             }
10376
10377             arguments.callee.anim = wrap.fxanim(a,
10378                 o,
10379                 'motion',
10380                 .5,
10381                 "easeOut", after);
10382         });
10383         return this;
10384     },
10385
10386         /**
10387          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10388          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10389          * The element must be removed from the DOM using the 'remove' config option if desired.
10390          * Usage:
10391          *<pre><code>
10392 // default
10393 el.puff();
10394
10395 // common config options shown with default values
10396 el.puff({
10397     easing: 'easeOut',
10398     duration: .5,
10399     remove: false,
10400     useDisplay: false
10401 });
10402 </code></pre>
10403          * @param {Object} options (optional) Object literal with any of the Fx config options
10404          * @return {Roo.Element} The Element
10405          */
10406     puff : function(o){
10407         var el = this.getFxEl();
10408         o = o || {};
10409
10410         el.queueFx(o, function(){
10411             this.clearOpacity();
10412             this.show();
10413
10414             // restore values after effect
10415             var r = this.getFxRestore();
10416             var st = this.dom.style;
10417
10418             var after = function(){
10419                 if(o.useDisplay){
10420                     el.setDisplayed(false);
10421                 }else{
10422                     el.hide();
10423                 }
10424
10425                 el.clearOpacity();
10426
10427                 el.setPositioning(r.pos);
10428                 st.width = r.width;
10429                 st.height = r.height;
10430                 st.fontSize = '';
10431                 el.afterFx(o);
10432             };
10433
10434             var width = this.getWidth();
10435             var height = this.getHeight();
10436
10437             arguments.callee.anim = this.fxanim({
10438                     width : {to: this.adjustWidth(width * 2)},
10439                     height : {to: this.adjustHeight(height * 2)},
10440                     points : {by: [-(width * .5), -(height * .5)]},
10441                     opacity : {to: 0},
10442                     fontSize: {to:200, unit: "%"}
10443                 },
10444                 o,
10445                 'motion',
10446                 .5,
10447                 "easeOut", after);
10448         });
10449         return this;
10450     },
10451
10452         /**
10453          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10454          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10455          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10456          * Usage:
10457          *<pre><code>
10458 // default
10459 el.switchOff();
10460
10461 // all config options shown with default values
10462 el.switchOff({
10463     easing: 'easeIn',
10464     duration: .3,
10465     remove: false,
10466     useDisplay: false
10467 });
10468 </code></pre>
10469          * @param {Object} options (optional) Object literal with any of the Fx config options
10470          * @return {Roo.Element} The Element
10471          */
10472     switchOff : function(o){
10473         var el = this.getFxEl();
10474         o = o || {};
10475
10476         el.queueFx(o, function(){
10477             this.clearOpacity();
10478             this.clip();
10479
10480             // restore values after effect
10481             var r = this.getFxRestore();
10482             var st = this.dom.style;
10483
10484             var after = function(){
10485                 if(o.useDisplay){
10486                     el.setDisplayed(false);
10487                 }else{
10488                     el.hide();
10489                 }
10490
10491                 el.clearOpacity();
10492                 el.setPositioning(r.pos);
10493                 st.width = r.width;
10494                 st.height = r.height;
10495
10496                 el.afterFx(o);
10497             };
10498
10499             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10500                 this.clearOpacity();
10501                 (function(){
10502                     this.fxanim({
10503                         height:{to:1},
10504                         points:{by:[0, this.getHeight() * .5]}
10505                     }, o, 'motion', 0.3, 'easeIn', after);
10506                 }).defer(100, this);
10507             });
10508         });
10509         return this;
10510     },
10511
10512     /**
10513      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10514      * changed using the "attr" config option) and then fading back to the original color. If no original
10515      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10516      * Usage:
10517 <pre><code>
10518 // default: highlight background to yellow
10519 el.highlight();
10520
10521 // custom: highlight foreground text to blue for 2 seconds
10522 el.highlight("0000ff", { attr: 'color', duration: 2 });
10523
10524 // common config options shown with default values
10525 el.highlight("ffff9c", {
10526     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10527     endColor: (current color) or "ffffff",
10528     easing: 'easeIn',
10529     duration: 1
10530 });
10531 </code></pre>
10532      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10533      * @param {Object} options (optional) Object literal with any of the Fx config options
10534      * @return {Roo.Element} The Element
10535      */ 
10536     highlight : function(color, o){
10537         var el = this.getFxEl();
10538         o = o || {};
10539
10540         el.queueFx(o, function(){
10541             color = color || "ffff9c";
10542             attr = o.attr || "backgroundColor";
10543
10544             this.clearOpacity();
10545             this.show();
10546
10547             var origColor = this.getColor(attr);
10548             var restoreColor = this.dom.style[attr];
10549             endColor = (o.endColor || origColor) || "ffffff";
10550
10551             var after = function(){
10552                 el.dom.style[attr] = restoreColor;
10553                 el.afterFx(o);
10554             };
10555
10556             var a = {};
10557             a[attr] = {from: color, to: endColor};
10558             arguments.callee.anim = this.fxanim(a,
10559                 o,
10560                 'color',
10561                 1,
10562                 'easeIn', after);
10563         });
10564         return this;
10565     },
10566
10567    /**
10568     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10569     * Usage:
10570 <pre><code>
10571 // default: a single light blue ripple
10572 el.frame();
10573
10574 // custom: 3 red ripples lasting 3 seconds total
10575 el.frame("ff0000", 3, { duration: 3 });
10576
10577 // common config options shown with default values
10578 el.frame("C3DAF9", 1, {
10579     duration: 1 //duration of entire animation (not each individual ripple)
10580     // Note: Easing is not configurable and will be ignored if included
10581 });
10582 </code></pre>
10583     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10584     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10585     * @param {Object} options (optional) Object literal with any of the Fx config options
10586     * @return {Roo.Element} The Element
10587     */
10588     frame : function(color, count, o){
10589         var el = this.getFxEl();
10590         o = o || {};
10591
10592         el.queueFx(o, function(){
10593             color = color || "#C3DAF9";
10594             if(color.length == 6){
10595                 color = "#" + color;
10596             }
10597             count = count || 1;
10598             duration = o.duration || 1;
10599             this.show();
10600
10601             var b = this.getBox();
10602             var animFn = function(){
10603                 var proxy = this.createProxy({
10604
10605                      style:{
10606                         visbility:"hidden",
10607                         position:"absolute",
10608                         "z-index":"35000", // yee haw
10609                         border:"0px solid " + color
10610                      }
10611                   });
10612                 var scale = Roo.isBorderBox ? 2 : 1;
10613                 proxy.animate({
10614                     top:{from:b.y, to:b.y - 20},
10615                     left:{from:b.x, to:b.x - 20},
10616                     borderWidth:{from:0, to:10},
10617                     opacity:{from:1, to:0},
10618                     height:{from:b.height, to:(b.height + (20*scale))},
10619                     width:{from:b.width, to:(b.width + (20*scale))}
10620                 }, duration, function(){
10621                     proxy.remove();
10622                 });
10623                 if(--count > 0){
10624                      animFn.defer((duration/2)*1000, this);
10625                 }else{
10626                     el.afterFx(o);
10627                 }
10628             };
10629             animFn.call(this);
10630         });
10631         return this;
10632     },
10633
10634    /**
10635     * Creates a pause before any subsequent queued effects begin.  If there are
10636     * no effects queued after the pause it will have no effect.
10637     * Usage:
10638 <pre><code>
10639 el.pause(1);
10640 </code></pre>
10641     * @param {Number} seconds The length of time to pause (in seconds)
10642     * @return {Roo.Element} The Element
10643     */
10644     pause : function(seconds){
10645         var el = this.getFxEl();
10646         var o = {};
10647
10648         el.queueFx(o, function(){
10649             setTimeout(function(){
10650                 el.afterFx(o);
10651             }, seconds * 1000);
10652         });
10653         return this;
10654     },
10655
10656    /**
10657     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10658     * using the "endOpacity" config option.
10659     * Usage:
10660 <pre><code>
10661 // default: fade in from opacity 0 to 100%
10662 el.fadeIn();
10663
10664 // custom: fade in from opacity 0 to 75% over 2 seconds
10665 el.fadeIn({ endOpacity: .75, duration: 2});
10666
10667 // common config options shown with default values
10668 el.fadeIn({
10669     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10670     easing: 'easeOut',
10671     duration: .5
10672 });
10673 </code></pre>
10674     * @param {Object} options (optional) Object literal with any of the Fx config options
10675     * @return {Roo.Element} The Element
10676     */
10677     fadeIn : function(o){
10678         var el = this.getFxEl();
10679         o = o || {};
10680         el.queueFx(o, function(){
10681             this.setOpacity(0);
10682             this.fixDisplay();
10683             this.dom.style.visibility = 'visible';
10684             var to = o.endOpacity || 1;
10685             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10686                 o, null, .5, "easeOut", function(){
10687                 if(to == 1){
10688                     this.clearOpacity();
10689                 }
10690                 el.afterFx(o);
10691             });
10692         });
10693         return this;
10694     },
10695
10696    /**
10697     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10698     * using the "endOpacity" config option.
10699     * Usage:
10700 <pre><code>
10701 // default: fade out from the element's current opacity to 0
10702 el.fadeOut();
10703
10704 // custom: fade out from the element's current opacity to 25% over 2 seconds
10705 el.fadeOut({ endOpacity: .25, duration: 2});
10706
10707 // common config options shown with default values
10708 el.fadeOut({
10709     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10710     easing: 'easeOut',
10711     duration: .5
10712     remove: false,
10713     useDisplay: false
10714 });
10715 </code></pre>
10716     * @param {Object} options (optional) Object literal with any of the Fx config options
10717     * @return {Roo.Element} The Element
10718     */
10719     fadeOut : function(o){
10720         var el = this.getFxEl();
10721         o = o || {};
10722         el.queueFx(o, function(){
10723             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10724                 o, null, .5, "easeOut", function(){
10725                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10726                      this.dom.style.display = "none";
10727                 }else{
10728                      this.dom.style.visibility = "hidden";
10729                 }
10730                 this.clearOpacity();
10731                 el.afterFx(o);
10732             });
10733         });
10734         return this;
10735     },
10736
10737    /**
10738     * Animates the transition of an element's dimensions from a starting height/width
10739     * to an ending height/width.
10740     * Usage:
10741 <pre><code>
10742 // change height and width to 100x100 pixels
10743 el.scale(100, 100);
10744
10745 // common config options shown with default values.  The height and width will default to
10746 // the element's existing values if passed as null.
10747 el.scale(
10748     [element's width],
10749     [element's height], {
10750     easing: 'easeOut',
10751     duration: .35
10752 });
10753 </code></pre>
10754     * @param {Number} width  The new width (pass undefined to keep the original width)
10755     * @param {Number} height  The new height (pass undefined to keep the original height)
10756     * @param {Object} options (optional) Object literal with any of the Fx config options
10757     * @return {Roo.Element} The Element
10758     */
10759     scale : function(w, h, o){
10760         this.shift(Roo.apply({}, o, {
10761             width: w,
10762             height: h
10763         }));
10764         return this;
10765     },
10766
10767    /**
10768     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10769     * Any of these properties not specified in the config object will not be changed.  This effect 
10770     * requires that at least one new dimension, position or opacity setting must be passed in on
10771     * the config object in order for the function to have any effect.
10772     * Usage:
10773 <pre><code>
10774 // slide the element horizontally to x position 200 while changing the height and opacity
10775 el.shift({ x: 200, height: 50, opacity: .8 });
10776
10777 // common config options shown with default values.
10778 el.shift({
10779     width: [element's width],
10780     height: [element's height],
10781     x: [element's x position],
10782     y: [element's y position],
10783     opacity: [element's opacity],
10784     easing: 'easeOut',
10785     duration: .35
10786 });
10787 </code></pre>
10788     * @param {Object} options  Object literal with any of the Fx config options
10789     * @return {Roo.Element} The Element
10790     */
10791     shift : function(o){
10792         var el = this.getFxEl();
10793         o = o || {};
10794         el.queueFx(o, function(){
10795             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10796             if(w !== undefined){
10797                 a.width = {to: this.adjustWidth(w)};
10798             }
10799             if(h !== undefined){
10800                 a.height = {to: this.adjustHeight(h)};
10801             }
10802             if(x !== undefined || y !== undefined){
10803                 a.points = {to: [
10804                     x !== undefined ? x : this.getX(),
10805                     y !== undefined ? y : this.getY()
10806                 ]};
10807             }
10808             if(op !== undefined){
10809                 a.opacity = {to: op};
10810             }
10811             if(o.xy !== undefined){
10812                 a.points = {to: o.xy};
10813             }
10814             arguments.callee.anim = this.fxanim(a,
10815                 o, 'motion', .35, "easeOut", function(){
10816                 el.afterFx(o);
10817             });
10818         });
10819         return this;
10820     },
10821
10822         /**
10823          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10824          * ending point of the effect.
10825          * Usage:
10826          *<pre><code>
10827 // default: slide the element downward while fading out
10828 el.ghost();
10829
10830 // custom: slide the element out to the right with a 2-second duration
10831 el.ghost('r', { duration: 2 });
10832
10833 // common config options shown with default values
10834 el.ghost('b', {
10835     easing: 'easeOut',
10836     duration: .5
10837     remove: false,
10838     useDisplay: false
10839 });
10840 </code></pre>
10841          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10842          * @param {Object} options (optional) Object literal with any of the Fx config options
10843          * @return {Roo.Element} The Element
10844          */
10845     ghost : function(anchor, o){
10846         var el = this.getFxEl();
10847         o = o || {};
10848
10849         el.queueFx(o, function(){
10850             anchor = anchor || "b";
10851
10852             // restore values after effect
10853             var r = this.getFxRestore();
10854             var w = this.getWidth(),
10855                 h = this.getHeight();
10856
10857             var st = this.dom.style;
10858
10859             var after = function(){
10860                 if(o.useDisplay){
10861                     el.setDisplayed(false);
10862                 }else{
10863                     el.hide();
10864                 }
10865
10866                 el.clearOpacity();
10867                 el.setPositioning(r.pos);
10868                 st.width = r.width;
10869                 st.height = r.height;
10870
10871                 el.afterFx(o);
10872             };
10873
10874             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10875             switch(anchor.toLowerCase()){
10876                 case "t":
10877                     pt.by = [0, -h];
10878                 break;
10879                 case "l":
10880                     pt.by = [-w, 0];
10881                 break;
10882                 case "r":
10883                     pt.by = [w, 0];
10884                 break;
10885                 case "b":
10886                     pt.by = [0, h];
10887                 break;
10888                 case "tl":
10889                     pt.by = [-w, -h];
10890                 break;
10891                 case "bl":
10892                     pt.by = [-w, h];
10893                 break;
10894                 case "br":
10895                     pt.by = [w, h];
10896                 break;
10897                 case "tr":
10898                     pt.by = [w, -h];
10899                 break;
10900             }
10901
10902             arguments.callee.anim = this.fxanim(a,
10903                 o,
10904                 'motion',
10905                 .5,
10906                 "easeOut", after);
10907         });
10908         return this;
10909     },
10910
10911         /**
10912          * Ensures that all effects queued after syncFx is called on the element are
10913          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10914          * @return {Roo.Element} The Element
10915          */
10916     syncFx : function(){
10917         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10918             block : false,
10919             concurrent : true,
10920             stopFx : false
10921         });
10922         return this;
10923     },
10924
10925         /**
10926          * Ensures that all effects queued after sequenceFx is called on the element are
10927          * run in sequence.  This is the opposite of {@link #syncFx}.
10928          * @return {Roo.Element} The Element
10929          */
10930     sequenceFx : function(){
10931         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10932             block : false,
10933             concurrent : false,
10934             stopFx : false
10935         });
10936         return this;
10937     },
10938
10939         /* @private */
10940     nextFx : function(){
10941         var ef = this.fxQueue[0];
10942         if(ef){
10943             ef.call(this);
10944         }
10945     },
10946
10947         /**
10948          * Returns true if the element has any effects actively running or queued, else returns false.
10949          * @return {Boolean} True if element has active effects, else false
10950          */
10951     hasActiveFx : function(){
10952         return this.fxQueue && this.fxQueue[0];
10953     },
10954
10955         /**
10956          * Stops any running effects and clears the element's internal effects queue if it contains
10957          * any additional effects that haven't started yet.
10958          * @return {Roo.Element} The Element
10959          */
10960     stopFx : function(){
10961         if(this.hasActiveFx()){
10962             var cur = this.fxQueue[0];
10963             if(cur && cur.anim && cur.anim.isAnimated()){
10964                 this.fxQueue = [cur]; // clear out others
10965                 cur.anim.stop(true);
10966             }
10967         }
10968         return this;
10969     },
10970
10971         /* @private */
10972     beforeFx : function(o){
10973         if(this.hasActiveFx() && !o.concurrent){
10974            if(o.stopFx){
10975                this.stopFx();
10976                return true;
10977            }
10978            return false;
10979         }
10980         return true;
10981     },
10982
10983         /**
10984          * Returns true if the element is currently blocking so that no other effect can be queued
10985          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10986          * used to ensure that an effect initiated by a user action runs to completion prior to the
10987          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10988          * @return {Boolean} True if blocking, else false
10989          */
10990     hasFxBlock : function(){
10991         var q = this.fxQueue;
10992         return q && q[0] && q[0].block;
10993     },
10994
10995         /* @private */
10996     queueFx : function(o, fn){
10997         if(!this.fxQueue){
10998             this.fxQueue = [];
10999         }
11000         if(!this.hasFxBlock()){
11001             Roo.applyIf(o, this.fxDefaults);
11002             if(!o.concurrent){
11003                 var run = this.beforeFx(o);
11004                 fn.block = o.block;
11005                 this.fxQueue.push(fn);
11006                 if(run){
11007                     this.nextFx();
11008                 }
11009             }else{
11010                 fn.call(this);
11011             }
11012         }
11013         return this;
11014     },
11015
11016         /* @private */
11017     fxWrap : function(pos, o, vis){
11018         var wrap;
11019         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11020             var wrapXY;
11021             if(o.fixPosition){
11022                 wrapXY = this.getXY();
11023             }
11024             var div = document.createElement("div");
11025             div.style.visibility = vis;
11026             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11027             wrap.setPositioning(pos);
11028             if(wrap.getStyle("position") == "static"){
11029                 wrap.position("relative");
11030             }
11031             this.clearPositioning('auto');
11032             wrap.clip();
11033             wrap.dom.appendChild(this.dom);
11034             if(wrapXY){
11035                 wrap.setXY(wrapXY);
11036             }
11037         }
11038         return wrap;
11039     },
11040
11041         /* @private */
11042     fxUnwrap : function(wrap, pos, o){
11043         this.clearPositioning();
11044         this.setPositioning(pos);
11045         if(!o.wrap){
11046             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11047             wrap.remove();
11048         }
11049     },
11050
11051         /* @private */
11052     getFxRestore : function(){
11053         var st = this.dom.style;
11054         return {pos: this.getPositioning(), width: st.width, height : st.height};
11055     },
11056
11057         /* @private */
11058     afterFx : function(o){
11059         if(o.afterStyle){
11060             this.applyStyles(o.afterStyle);
11061         }
11062         if(o.afterCls){
11063             this.addClass(o.afterCls);
11064         }
11065         if(o.remove === true){
11066             this.remove();
11067         }
11068         Roo.callback(o.callback, o.scope, [this]);
11069         if(!o.concurrent){
11070             this.fxQueue.shift();
11071             this.nextFx();
11072         }
11073     },
11074
11075         /* @private */
11076     getFxEl : function(){ // support for composite element fx
11077         return Roo.get(this.dom);
11078     },
11079
11080         /* @private */
11081     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11082         animType = animType || 'run';
11083         opt = opt || {};
11084         var anim = Roo.lib.Anim[animType](
11085             this.dom, args,
11086             (opt.duration || defaultDur) || .35,
11087             (opt.easing || defaultEase) || 'easeOut',
11088             function(){
11089                 Roo.callback(cb, this);
11090             },
11091             this
11092         );
11093         opt.anim = anim;
11094         return anim;
11095     }
11096 };
11097
11098 // backwords compat
11099 Roo.Fx.resize = Roo.Fx.scale;
11100
11101 //When included, Roo.Fx is automatically applied to Element so that all basic
11102 //effects are available directly via the Element API
11103 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11104  * Based on:
11105  * Ext JS Library 1.1.1
11106  * Copyright(c) 2006-2007, Ext JS, LLC.
11107  *
11108  * Originally Released Under LGPL - original licence link has changed is not relivant.
11109  *
11110  * Fork - LGPL
11111  * <script type="text/javascript">
11112  */
11113
11114
11115 /**
11116  * @class Roo.CompositeElement
11117  * Standard composite class. Creates a Roo.Element for every element in the collection.
11118  * <br><br>
11119  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11120  * actions will be performed on all the elements in this collection.</b>
11121  * <br><br>
11122  * All methods return <i>this</i> and can be chained.
11123  <pre><code>
11124  var els = Roo.select("#some-el div.some-class", true);
11125  // or select directly from an existing element
11126  var el = Roo.get('some-el');
11127  el.select('div.some-class', true);
11128
11129  els.setWidth(100); // all elements become 100 width
11130  els.hide(true); // all elements fade out and hide
11131  // or
11132  els.setWidth(100).hide(true);
11133  </code></pre>
11134  */
11135 Roo.CompositeElement = function(els){
11136     this.elements = [];
11137     this.addElements(els);
11138 };
11139 Roo.CompositeElement.prototype = {
11140     isComposite: true,
11141     addElements : function(els){
11142         if(!els) {
11143             return this;
11144         }
11145         if(typeof els == "string"){
11146             els = Roo.Element.selectorFunction(els);
11147         }
11148         var yels = this.elements;
11149         var index = yels.length-1;
11150         for(var i = 0, len = els.length; i < len; i++) {
11151                 yels[++index] = Roo.get(els[i]);
11152         }
11153         return this;
11154     },
11155
11156     /**
11157     * Clears this composite and adds the elements returned by the passed selector.
11158     * @param {String/Array} els A string CSS selector, an array of elements or an element
11159     * @return {CompositeElement} this
11160     */
11161     fill : function(els){
11162         this.elements = [];
11163         this.add(els);
11164         return this;
11165     },
11166
11167     /**
11168     * Filters this composite to only elements that match the passed selector.
11169     * @param {String} selector A string CSS selector
11170     * @param {Boolean} inverse return inverse filter (not matches)
11171     * @return {CompositeElement} this
11172     */
11173     filter : function(selector, inverse){
11174         var els = [];
11175         inverse = inverse || false;
11176         this.each(function(el){
11177             var match = inverse ? !el.is(selector) : el.is(selector);
11178             if(match){
11179                 els[els.length] = el.dom;
11180             }
11181         });
11182         this.fill(els);
11183         return this;
11184     },
11185
11186     invoke : function(fn, args){
11187         var els = this.elements;
11188         for(var i = 0, len = els.length; i < len; i++) {
11189                 Roo.Element.prototype[fn].apply(els[i], args);
11190         }
11191         return this;
11192     },
11193     /**
11194     * Adds elements to this composite.
11195     * @param {String/Array} els A string CSS selector, an array of elements or an element
11196     * @return {CompositeElement} this
11197     */
11198     add : function(els){
11199         if(typeof els == "string"){
11200             this.addElements(Roo.Element.selectorFunction(els));
11201         }else if(els.length !== undefined){
11202             this.addElements(els);
11203         }else{
11204             this.addElements([els]);
11205         }
11206         return this;
11207     },
11208     /**
11209     * Calls the passed function passing (el, this, index) for each element in this composite.
11210     * @param {Function} fn The function to call
11211     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11212     * @return {CompositeElement} this
11213     */
11214     each : function(fn, scope){
11215         var els = this.elements;
11216         for(var i = 0, len = els.length; i < len; i++){
11217             if(fn.call(scope || els[i], els[i], this, i) === false) {
11218                 break;
11219             }
11220         }
11221         return this;
11222     },
11223
11224     /**
11225      * Returns the Element object at the specified index
11226      * @param {Number} index
11227      * @return {Roo.Element}
11228      */
11229     item : function(index){
11230         return this.elements[index] || null;
11231     },
11232
11233     /**
11234      * Returns the first Element
11235      * @return {Roo.Element}
11236      */
11237     first : function(){
11238         return this.item(0);
11239     },
11240
11241     /**
11242      * Returns the last Element
11243      * @return {Roo.Element}
11244      */
11245     last : function(){
11246         return this.item(this.elements.length-1);
11247     },
11248
11249     /**
11250      * Returns the number of elements in this composite
11251      * @return Number
11252      */
11253     getCount : function(){
11254         return this.elements.length;
11255     },
11256
11257     /**
11258      * Returns true if this composite contains the passed element
11259      * @return Boolean
11260      */
11261     contains : function(el){
11262         return this.indexOf(el) !== -1;
11263     },
11264
11265     /**
11266      * Returns true if this composite contains the passed element
11267      * @return Boolean
11268      */
11269     indexOf : function(el){
11270         return this.elements.indexOf(Roo.get(el));
11271     },
11272
11273
11274     /**
11275     * Removes the specified element(s).
11276     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11277     * or an array of any of those.
11278     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11279     * @return {CompositeElement} this
11280     */
11281     removeElement : function(el, removeDom){
11282         if(el instanceof Array){
11283             for(var i = 0, len = el.length; i < len; i++){
11284                 this.removeElement(el[i]);
11285             }
11286             return this;
11287         }
11288         var index = typeof el == 'number' ? el : this.indexOf(el);
11289         if(index !== -1){
11290             if(removeDom){
11291                 var d = this.elements[index];
11292                 if(d.dom){
11293                     d.remove();
11294                 }else{
11295                     d.parentNode.removeChild(d);
11296                 }
11297             }
11298             this.elements.splice(index, 1);
11299         }
11300         return this;
11301     },
11302
11303     /**
11304     * Replaces the specified element with the passed element.
11305     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11306     * to replace.
11307     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11308     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11309     * @return {CompositeElement} this
11310     */
11311     replaceElement : function(el, replacement, domReplace){
11312         var index = typeof el == 'number' ? el : this.indexOf(el);
11313         if(index !== -1){
11314             if(domReplace){
11315                 this.elements[index].replaceWith(replacement);
11316             }else{
11317                 this.elements.splice(index, 1, Roo.get(replacement))
11318             }
11319         }
11320         return this;
11321     },
11322
11323     /**
11324      * Removes all elements.
11325      */
11326     clear : function(){
11327         this.elements = [];
11328     }
11329 };
11330 (function(){
11331     Roo.CompositeElement.createCall = function(proto, fnName){
11332         if(!proto[fnName]){
11333             proto[fnName] = function(){
11334                 return this.invoke(fnName, arguments);
11335             };
11336         }
11337     };
11338     for(var fnName in Roo.Element.prototype){
11339         if(typeof Roo.Element.prototype[fnName] == "function"){
11340             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11341         }
11342     };
11343 })();
11344 /*
11345  * Based on:
11346  * Ext JS Library 1.1.1
11347  * Copyright(c) 2006-2007, Ext JS, LLC.
11348  *
11349  * Originally Released Under LGPL - original licence link has changed is not relivant.
11350  *
11351  * Fork - LGPL
11352  * <script type="text/javascript">
11353  */
11354
11355 /**
11356  * @class Roo.CompositeElementLite
11357  * @extends Roo.CompositeElement
11358  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11359  <pre><code>
11360  var els = Roo.select("#some-el div.some-class");
11361  // or select directly from an existing element
11362  var el = Roo.get('some-el');
11363  el.select('div.some-class');
11364
11365  els.setWidth(100); // all elements become 100 width
11366  els.hide(true); // all elements fade out and hide
11367  // or
11368  els.setWidth(100).hide(true);
11369  </code></pre><br><br>
11370  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11371  * actions will be performed on all the elements in this collection.</b>
11372  */
11373 Roo.CompositeElementLite = function(els){
11374     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11375     this.el = new Roo.Element.Flyweight();
11376 };
11377 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11378     addElements : function(els){
11379         if(els){
11380             if(els instanceof Array){
11381                 this.elements = this.elements.concat(els);
11382             }else{
11383                 var yels = this.elements;
11384                 var index = yels.length-1;
11385                 for(var i = 0, len = els.length; i < len; i++) {
11386                     yels[++index] = els[i];
11387                 }
11388             }
11389         }
11390         return this;
11391     },
11392     invoke : function(fn, args){
11393         var els = this.elements;
11394         var el = this.el;
11395         for(var i = 0, len = els.length; i < len; i++) {
11396             el.dom = els[i];
11397                 Roo.Element.prototype[fn].apply(el, args);
11398         }
11399         return this;
11400     },
11401     /**
11402      * Returns a flyweight Element of the dom element object at the specified index
11403      * @param {Number} index
11404      * @return {Roo.Element}
11405      */
11406     item : function(index){
11407         if(!this.elements[index]){
11408             return null;
11409         }
11410         this.el.dom = this.elements[index];
11411         return this.el;
11412     },
11413
11414     // fixes scope with flyweight
11415     addListener : function(eventName, handler, scope, opt){
11416         var els = this.elements;
11417         for(var i = 0, len = els.length; i < len; i++) {
11418             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11419         }
11420         return this;
11421     },
11422
11423     /**
11424     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11425     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11426     * a reference to the dom node, use el.dom.</b>
11427     * @param {Function} fn The function to call
11428     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11429     * @return {CompositeElement} this
11430     */
11431     each : function(fn, scope){
11432         var els = this.elements;
11433         var el = this.el;
11434         for(var i = 0, len = els.length; i < len; i++){
11435             el.dom = els[i];
11436                 if(fn.call(scope || el, el, this, i) === false){
11437                 break;
11438             }
11439         }
11440         return this;
11441     },
11442
11443     indexOf : function(el){
11444         return this.elements.indexOf(Roo.getDom(el));
11445     },
11446
11447     replaceElement : function(el, replacement, domReplace){
11448         var index = typeof el == 'number' ? el : this.indexOf(el);
11449         if(index !== -1){
11450             replacement = Roo.getDom(replacement);
11451             if(domReplace){
11452                 var d = this.elements[index];
11453                 d.parentNode.insertBefore(replacement, d);
11454                 d.parentNode.removeChild(d);
11455             }
11456             this.elements.splice(index, 1, replacement);
11457         }
11458         return this;
11459     }
11460 });
11461 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11462
11463 /*
11464  * Based on:
11465  * Ext JS Library 1.1.1
11466  * Copyright(c) 2006-2007, Ext JS, LLC.
11467  *
11468  * Originally Released Under LGPL - original licence link has changed is not relivant.
11469  *
11470  * Fork - LGPL
11471  * <script type="text/javascript">
11472  */
11473
11474  
11475
11476 /**
11477  * @class Roo.data.Connection
11478  * @extends Roo.util.Observable
11479  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11480  * either to a configured URL, or to a URL specified at request time.<br><br>
11481  * <p>
11482  * Requests made by this class are asynchronous, and will return immediately. No data from
11483  * the server will be available to the statement immediately following the {@link #request} call.
11484  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11485  * <p>
11486  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11487  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11488  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11489  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11490  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11491  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11492  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11493  * standard DOM methods.
11494  * @constructor
11495  * @param {Object} config a configuration object.
11496  */
11497 Roo.data.Connection = function(config){
11498     Roo.apply(this, config);
11499     this.addEvents({
11500         /**
11501          * @event beforerequest
11502          * Fires before a network request is made to retrieve a data object.
11503          * @param {Connection} conn This Connection object.
11504          * @param {Object} options The options config object passed to the {@link #request} method.
11505          */
11506         "beforerequest" : true,
11507         /**
11508          * @event requestcomplete
11509          * Fires if the request was successfully completed.
11510          * @param {Connection} conn This Connection object.
11511          * @param {Object} response The XHR object containing the response data.
11512          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11513          * @param {Object} options The options config object passed to the {@link #request} method.
11514          */
11515         "requestcomplete" : true,
11516         /**
11517          * @event requestexception
11518          * Fires if an error HTTP status was returned from the server.
11519          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11520          * @param {Connection} conn This Connection object.
11521          * @param {Object} response The XHR object containing the response data.
11522          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11523          * @param {Object} options The options config object passed to the {@link #request} method.
11524          */
11525         "requestexception" : true
11526     });
11527     Roo.data.Connection.superclass.constructor.call(this);
11528 };
11529
11530 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11531     /**
11532      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11533      */
11534     /**
11535      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11536      * extra parameters to each request made by this object. (defaults to undefined)
11537      */
11538     /**
11539      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11540      *  to each request made by this object. (defaults to undefined)
11541      */
11542     /**
11543      * @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)
11544      */
11545     /**
11546      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11547      */
11548     timeout : 30000,
11549     /**
11550      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11551      * @type Boolean
11552      */
11553     autoAbort:false,
11554
11555     /**
11556      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11557      * @type Boolean
11558      */
11559     disableCaching: true,
11560
11561     /**
11562      * Sends an HTTP request to a remote server.
11563      * @param {Object} options An object which may contain the following properties:<ul>
11564      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11565      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11566      * request, a url encoded string or a function to call to get either.</li>
11567      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11568      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11569      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11570      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11571      * <li>options {Object} The parameter to the request call.</li>
11572      * <li>success {Boolean} True if the request succeeded.</li>
11573      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11574      * </ul></li>
11575      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11576      * The callback is passed the following parameters:<ul>
11577      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11578      * <li>options {Object} The parameter to the request call.</li>
11579      * </ul></li>
11580      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11581      * The callback is passed the following parameters:<ul>
11582      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11583      * <li>options {Object} The parameter to the request call.</li>
11584      * </ul></li>
11585      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11586      * for the callback function. Defaults to the browser window.</li>
11587      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11588      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11589      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11590      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11591      * params for the post data. Any params will be appended to the URL.</li>
11592      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11593      * </ul>
11594      * @return {Number} transactionId
11595      */
11596     request : function(o){
11597         if(this.fireEvent("beforerequest", this, o) !== false){
11598             var p = o.params;
11599
11600             if(typeof p == "function"){
11601                 p = p.call(o.scope||window, o);
11602             }
11603             if(typeof p == "object"){
11604                 p = Roo.urlEncode(o.params);
11605             }
11606             if(this.extraParams){
11607                 var extras = Roo.urlEncode(this.extraParams);
11608                 p = p ? (p + '&' + extras) : extras;
11609             }
11610
11611             var url = o.url || this.url;
11612             if(typeof url == 'function'){
11613                 url = url.call(o.scope||window, o);
11614             }
11615
11616             if(o.form){
11617                 var form = Roo.getDom(o.form);
11618                 url = url || form.action;
11619
11620                 var enctype = form.getAttribute("enctype");
11621                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11622                     return this.doFormUpload(o, p, url);
11623                 }
11624                 var f = Roo.lib.Ajax.serializeForm(form);
11625                 p = p ? (p + '&' + f) : f;
11626             }
11627
11628             var hs = o.headers;
11629             if(this.defaultHeaders){
11630                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11631                 if(!o.headers){
11632                     o.headers = hs;
11633                 }
11634             }
11635
11636             var cb = {
11637                 success: this.handleResponse,
11638                 failure: this.handleFailure,
11639                 scope: this,
11640                 argument: {options: o},
11641                 timeout : o.timeout || this.timeout
11642             };
11643
11644             var method = o.method||this.method||(p ? "POST" : "GET");
11645
11646             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11647                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11648             }
11649
11650             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11651                 if(o.autoAbort){
11652                     this.abort();
11653                 }
11654             }else if(this.autoAbort !== false){
11655                 this.abort();
11656             }
11657
11658             if((method == 'GET' && p) || o.xmlData){
11659                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11660                 p = '';
11661             }
11662             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11663             return this.transId;
11664         }else{
11665             Roo.callback(o.callback, o.scope, [o, null, null]);
11666             return null;
11667         }
11668     },
11669
11670     /**
11671      * Determine whether this object has a request outstanding.
11672      * @param {Number} transactionId (Optional) defaults to the last transaction
11673      * @return {Boolean} True if there is an outstanding request.
11674      */
11675     isLoading : function(transId){
11676         if(transId){
11677             return Roo.lib.Ajax.isCallInProgress(transId);
11678         }else{
11679             return this.transId ? true : false;
11680         }
11681     },
11682
11683     /**
11684      * Aborts any outstanding request.
11685      * @param {Number} transactionId (Optional) defaults to the last transaction
11686      */
11687     abort : function(transId){
11688         if(transId || this.isLoading()){
11689             Roo.lib.Ajax.abort(transId || this.transId);
11690         }
11691     },
11692
11693     // private
11694     handleResponse : function(response){
11695         this.transId = false;
11696         var options = response.argument.options;
11697         response.argument = options ? options.argument : null;
11698         this.fireEvent("requestcomplete", this, response, options);
11699         Roo.callback(options.success, options.scope, [response, options]);
11700         Roo.callback(options.callback, options.scope, [options, true, response]);
11701     },
11702
11703     // private
11704     handleFailure : function(response, e){
11705         this.transId = false;
11706         var options = response.argument.options;
11707         response.argument = options ? options.argument : null;
11708         this.fireEvent("requestexception", this, response, options, e);
11709         Roo.callback(options.failure, options.scope, [response, options]);
11710         Roo.callback(options.callback, options.scope, [options, false, response]);
11711     },
11712
11713     // private
11714     doFormUpload : function(o, ps, url){
11715         var id = Roo.id();
11716         var frame = document.createElement('iframe');
11717         frame.id = id;
11718         frame.name = id;
11719         frame.className = 'x-hidden';
11720         if(Roo.isIE){
11721             frame.src = Roo.SSL_SECURE_URL;
11722         }
11723         document.body.appendChild(frame);
11724
11725         if(Roo.isIE){
11726            document.frames[id].name = id;
11727         }
11728
11729         var form = Roo.getDom(o.form);
11730         form.target = id;
11731         form.method = 'POST';
11732         form.enctype = form.encoding = 'multipart/form-data';
11733         if(url){
11734             form.action = url;
11735         }
11736
11737         var hiddens, hd;
11738         if(ps){ // add dynamic params
11739             hiddens = [];
11740             ps = Roo.urlDecode(ps, false);
11741             for(var k in ps){
11742                 if(ps.hasOwnProperty(k)){
11743                     hd = document.createElement('input');
11744                     hd.type = 'hidden';
11745                     hd.name = k;
11746                     hd.value = ps[k];
11747                     form.appendChild(hd);
11748                     hiddens.push(hd);
11749                 }
11750             }
11751         }
11752
11753         function cb(){
11754             var r = {  // bogus response object
11755                 responseText : '',
11756                 responseXML : null
11757             };
11758
11759             r.argument = o ? o.argument : null;
11760
11761             try { //
11762                 var doc;
11763                 if(Roo.isIE){
11764                     doc = frame.contentWindow.document;
11765                 }else {
11766                     doc = (frame.contentDocument || window.frames[id].document);
11767                 }
11768                 if(doc && doc.body){
11769                     r.responseText = doc.body.innerHTML;
11770                 }
11771                 if(doc && doc.XMLDocument){
11772                     r.responseXML = doc.XMLDocument;
11773                 }else {
11774                     r.responseXML = doc;
11775                 }
11776             }
11777             catch(e) {
11778                 // ignore
11779             }
11780
11781             Roo.EventManager.removeListener(frame, 'load', cb, this);
11782
11783             this.fireEvent("requestcomplete", this, r, o);
11784             Roo.callback(o.success, o.scope, [r, o]);
11785             Roo.callback(o.callback, o.scope, [o, true, r]);
11786
11787             setTimeout(function(){document.body.removeChild(frame);}, 100);
11788         }
11789
11790         Roo.EventManager.on(frame, 'load', cb, this);
11791         form.submit();
11792
11793         if(hiddens){ // remove dynamic params
11794             for(var i = 0, len = hiddens.length; i < len; i++){
11795                 form.removeChild(hiddens[i]);
11796             }
11797         }
11798     }
11799 });
11800 /*
11801  * Based on:
11802  * Ext JS Library 1.1.1
11803  * Copyright(c) 2006-2007, Ext JS, LLC.
11804  *
11805  * Originally Released Under LGPL - original licence link has changed is not relivant.
11806  *
11807  * Fork - LGPL
11808  * <script type="text/javascript">
11809  */
11810  
11811 /**
11812  * Global Ajax request class.
11813  * 
11814  * @class Roo.Ajax
11815  * @extends Roo.data.Connection
11816  * @static
11817  * 
11818  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11819  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11820  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11821  * @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)
11822  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11823  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11824  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11825  */
11826 Roo.Ajax = new Roo.data.Connection({
11827     // fix up the docs
11828     /**
11829      * @scope Roo.Ajax
11830      * @type {Boolear} 
11831      */
11832     autoAbort : false,
11833
11834     /**
11835      * Serialize the passed form into a url encoded string
11836      * @scope Roo.Ajax
11837      * @param {String/HTMLElement} form
11838      * @return {String}
11839      */
11840     serializeForm : function(form){
11841         return Roo.lib.Ajax.serializeForm(form);
11842     }
11843 });/*
11844  * Based on:
11845  * Ext JS Library 1.1.1
11846  * Copyright(c) 2006-2007, Ext JS, LLC.
11847  *
11848  * Originally Released Under LGPL - original licence link has changed is not relivant.
11849  *
11850  * Fork - LGPL
11851  * <script type="text/javascript">
11852  */
11853
11854  
11855 /**
11856  * @class Roo.UpdateManager
11857  * @extends Roo.util.Observable
11858  * Provides AJAX-style update for Element object.<br><br>
11859  * Usage:<br>
11860  * <pre><code>
11861  * // Get it from a Roo.Element object
11862  * var el = Roo.get("foo");
11863  * var mgr = el.getUpdateManager();
11864  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11865  * ...
11866  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11867  * <br>
11868  * // or directly (returns the same UpdateManager instance)
11869  * var mgr = new Roo.UpdateManager("myElementId");
11870  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11871  * mgr.on("update", myFcnNeedsToKnow);
11872  * <br>
11873    // short handed call directly from the element object
11874    Roo.get("foo").load({
11875         url: "bar.php",
11876         scripts:true,
11877         params: "for=bar",
11878         text: "Loading Foo..."
11879    });
11880  * </code></pre>
11881  * @constructor
11882  * Create new UpdateManager directly.
11883  * @param {String/HTMLElement/Roo.Element} el The element to update
11884  * @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).
11885  */
11886 Roo.UpdateManager = function(el, forceNew){
11887     el = Roo.get(el);
11888     if(!forceNew && el.updateManager){
11889         return el.updateManager;
11890     }
11891     /**
11892      * The Element object
11893      * @type Roo.Element
11894      */
11895     this.el = el;
11896     /**
11897      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11898      * @type String
11899      */
11900     this.defaultUrl = null;
11901
11902     this.addEvents({
11903         /**
11904          * @event beforeupdate
11905          * Fired before an update is made, return false from your handler and the update is cancelled.
11906          * @param {Roo.Element} el
11907          * @param {String/Object/Function} url
11908          * @param {String/Object} params
11909          */
11910         "beforeupdate": true,
11911         /**
11912          * @event update
11913          * Fired after successful update is made.
11914          * @param {Roo.Element} el
11915          * @param {Object} oResponseObject The response Object
11916          */
11917         "update": true,
11918         /**
11919          * @event failure
11920          * Fired on update failure.
11921          * @param {Roo.Element} el
11922          * @param {Object} oResponseObject The response Object
11923          */
11924         "failure": true
11925     });
11926     var d = Roo.UpdateManager.defaults;
11927     /**
11928      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11929      * @type String
11930      */
11931     this.sslBlankUrl = d.sslBlankUrl;
11932     /**
11933      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11934      * @type Boolean
11935      */
11936     this.disableCaching = d.disableCaching;
11937     /**
11938      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11939      * @type String
11940      */
11941     this.indicatorText = d.indicatorText;
11942     /**
11943      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11944      * @type String
11945      */
11946     this.showLoadIndicator = d.showLoadIndicator;
11947     /**
11948      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11949      * @type Number
11950      */
11951     this.timeout = d.timeout;
11952
11953     /**
11954      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11955      * @type Boolean
11956      */
11957     this.loadScripts = d.loadScripts;
11958
11959     /**
11960      * Transaction object of current executing transaction
11961      */
11962     this.transaction = null;
11963
11964     /**
11965      * @private
11966      */
11967     this.autoRefreshProcId = null;
11968     /**
11969      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11970      * @type Function
11971      */
11972     this.refreshDelegate = this.refresh.createDelegate(this);
11973     /**
11974      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11975      * @type Function
11976      */
11977     this.updateDelegate = this.update.createDelegate(this);
11978     /**
11979      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11980      * @type Function
11981      */
11982     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11983     /**
11984      * @private
11985      */
11986     this.successDelegate = this.processSuccess.createDelegate(this);
11987     /**
11988      * @private
11989      */
11990     this.failureDelegate = this.processFailure.createDelegate(this);
11991
11992     if(!this.renderer){
11993      /**
11994       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11995       */
11996     this.renderer = new Roo.UpdateManager.BasicRenderer();
11997     }
11998     
11999     Roo.UpdateManager.superclass.constructor.call(this);
12000 };
12001
12002 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12003     /**
12004      * Get the Element this UpdateManager is bound to
12005      * @return {Roo.Element} The element
12006      */
12007     getEl : function(){
12008         return this.el;
12009     },
12010     /**
12011      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12012      * @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:
12013 <pre><code>
12014 um.update({<br/>
12015     url: "your-url.php",<br/>
12016     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12017     callback: yourFunction,<br/>
12018     scope: yourObject, //(optional scope)  <br/>
12019     discardUrl: false, <br/>
12020     nocache: false,<br/>
12021     text: "Loading...",<br/>
12022     timeout: 30,<br/>
12023     scripts: false<br/>
12024 });
12025 </code></pre>
12026      * The only required property is url. The optional properties nocache, text and scripts
12027      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12028      * @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}
12029      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12030      * @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.
12031      */
12032     update : function(url, params, callback, discardUrl){
12033         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12034             var method = this.method,
12035                 cfg;
12036             if(typeof url == "object"){ // must be config object
12037                 cfg = url;
12038                 url = cfg.url;
12039                 params = params || cfg.params;
12040                 callback = callback || cfg.callback;
12041                 discardUrl = discardUrl || cfg.discardUrl;
12042                 if(callback && cfg.scope){
12043                     callback = callback.createDelegate(cfg.scope);
12044                 }
12045                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12046                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12047                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12048                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12049                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12050             }
12051             this.showLoading();
12052             if(!discardUrl){
12053                 this.defaultUrl = url;
12054             }
12055             if(typeof url == "function"){
12056                 url = url.call(this);
12057             }
12058
12059             method = method || (params ? "POST" : "GET");
12060             if(method == "GET"){
12061                 url = this.prepareUrl(url);
12062             }
12063
12064             var o = Roo.apply(cfg ||{}, {
12065                 url : url,
12066                 params: params,
12067                 success: this.successDelegate,
12068                 failure: this.failureDelegate,
12069                 callback: undefined,
12070                 timeout: (this.timeout*1000),
12071                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12072             });
12073             Roo.log("updated manager called with timeout of " + o.timeout);
12074             this.transaction = Roo.Ajax.request(o);
12075         }
12076     },
12077
12078     /**
12079      * 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.
12080      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12081      * @param {String/HTMLElement} form The form Id or form element
12082      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12083      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12084      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12085      */
12086     formUpdate : function(form, url, reset, callback){
12087         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12088             if(typeof url == "function"){
12089                 url = url.call(this);
12090             }
12091             form = Roo.getDom(form);
12092             this.transaction = Roo.Ajax.request({
12093                 form: form,
12094                 url:url,
12095                 success: this.successDelegate,
12096                 failure: this.failureDelegate,
12097                 timeout: (this.timeout*1000),
12098                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12099             });
12100             this.showLoading.defer(1, this);
12101         }
12102     },
12103
12104     /**
12105      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12106      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12107      */
12108     refresh : function(callback){
12109         if(this.defaultUrl == null){
12110             return;
12111         }
12112         this.update(this.defaultUrl, null, callback, true);
12113     },
12114
12115     /**
12116      * Set this element to auto refresh.
12117      * @param {Number} interval How often to update (in seconds).
12118      * @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)
12119      * @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}
12120      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12121      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12122      */
12123     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12124         if(refreshNow){
12125             this.update(url || this.defaultUrl, params, callback, true);
12126         }
12127         if(this.autoRefreshProcId){
12128             clearInterval(this.autoRefreshProcId);
12129         }
12130         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12131     },
12132
12133     /**
12134      * Stop auto refresh on this element.
12135      */
12136      stopAutoRefresh : function(){
12137         if(this.autoRefreshProcId){
12138             clearInterval(this.autoRefreshProcId);
12139             delete this.autoRefreshProcId;
12140         }
12141     },
12142
12143     isAutoRefreshing : function(){
12144        return this.autoRefreshProcId ? true : false;
12145     },
12146     /**
12147      * Called to update the element to "Loading" state. Override to perform custom action.
12148      */
12149     showLoading : function(){
12150         if(this.showLoadIndicator){
12151             this.el.update(this.indicatorText);
12152         }
12153     },
12154
12155     /**
12156      * Adds unique parameter to query string if disableCaching = true
12157      * @private
12158      */
12159     prepareUrl : function(url){
12160         if(this.disableCaching){
12161             var append = "_dc=" + (new Date().getTime());
12162             if(url.indexOf("?") !== -1){
12163                 url += "&" + append;
12164             }else{
12165                 url += "?" + append;
12166             }
12167         }
12168         return url;
12169     },
12170
12171     /**
12172      * @private
12173      */
12174     processSuccess : function(response){
12175         this.transaction = null;
12176         if(response.argument.form && response.argument.reset){
12177             try{ // put in try/catch since some older FF releases had problems with this
12178                 response.argument.form.reset();
12179             }catch(e){}
12180         }
12181         if(this.loadScripts){
12182             this.renderer.render(this.el, response, this,
12183                 this.updateComplete.createDelegate(this, [response]));
12184         }else{
12185             this.renderer.render(this.el, response, this);
12186             this.updateComplete(response);
12187         }
12188     },
12189
12190     updateComplete : function(response){
12191         this.fireEvent("update", this.el, response);
12192         if(typeof response.argument.callback == "function"){
12193             response.argument.callback(this.el, true, response);
12194         }
12195     },
12196
12197     /**
12198      * @private
12199      */
12200     processFailure : function(response){
12201         this.transaction = null;
12202         this.fireEvent("failure", this.el, response);
12203         if(typeof response.argument.callback == "function"){
12204             response.argument.callback(this.el, false, response);
12205         }
12206     },
12207
12208     /**
12209      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12210      * @param {Object} renderer The object implementing the render() method
12211      */
12212     setRenderer : function(renderer){
12213         this.renderer = renderer;
12214     },
12215
12216     getRenderer : function(){
12217        return this.renderer;
12218     },
12219
12220     /**
12221      * Set the defaultUrl used for updates
12222      * @param {String/Function} defaultUrl The url or a function to call to get the url
12223      */
12224     setDefaultUrl : function(defaultUrl){
12225         this.defaultUrl = defaultUrl;
12226     },
12227
12228     /**
12229      * Aborts the executing transaction
12230      */
12231     abort : function(){
12232         if(this.transaction){
12233             Roo.Ajax.abort(this.transaction);
12234         }
12235     },
12236
12237     /**
12238      * Returns true if an update is in progress
12239      * @return {Boolean}
12240      */
12241     isUpdating : function(){
12242         if(this.transaction){
12243             return Roo.Ajax.isLoading(this.transaction);
12244         }
12245         return false;
12246     }
12247 });
12248
12249 /**
12250  * @class Roo.UpdateManager.defaults
12251  * @static (not really - but it helps the doc tool)
12252  * The defaults collection enables customizing the default properties of UpdateManager
12253  */
12254    Roo.UpdateManager.defaults = {
12255        /**
12256          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12257          * @type Number
12258          */
12259          timeout : 30,
12260
12261          /**
12262          * True to process scripts by default (Defaults to false).
12263          * @type Boolean
12264          */
12265         loadScripts : false,
12266
12267         /**
12268         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12269         * @type String
12270         */
12271         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12272         /**
12273          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12274          * @type Boolean
12275          */
12276         disableCaching : false,
12277         /**
12278          * Whether to show indicatorText when loading (Defaults to true).
12279          * @type Boolean
12280          */
12281         showLoadIndicator : true,
12282         /**
12283          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12284          * @type String
12285          */
12286         indicatorText : '<div class="loading-indicator">Loading...</div>'
12287    };
12288
12289 /**
12290  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12291  *Usage:
12292  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12293  * @param {String/HTMLElement/Roo.Element} el The element to update
12294  * @param {String} url The url
12295  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12296  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12297  * @static
12298  * @deprecated
12299  * @member Roo.UpdateManager
12300  */
12301 Roo.UpdateManager.updateElement = function(el, url, params, options){
12302     var um = Roo.get(el, true).getUpdateManager();
12303     Roo.apply(um, options);
12304     um.update(url, params, options ? options.callback : null);
12305 };
12306 // alias for backwards compat
12307 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12308 /**
12309  * @class Roo.UpdateManager.BasicRenderer
12310  * Default Content renderer. Updates the elements innerHTML with the responseText.
12311  */
12312 Roo.UpdateManager.BasicRenderer = function(){};
12313
12314 Roo.UpdateManager.BasicRenderer.prototype = {
12315     /**
12316      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12317      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12318      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12319      * @param {Roo.Element} el The element being rendered
12320      * @param {Object} response The YUI Connect response object
12321      * @param {UpdateManager} updateManager The calling update manager
12322      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12323      */
12324      render : function(el, response, updateManager, callback){
12325         el.update(response.responseText, updateManager.loadScripts, callback);
12326     }
12327 };
12328 /*
12329  * Based on:
12330  * Roo JS
12331  * (c)) Alan Knowles
12332  * Licence : LGPL
12333  */
12334
12335
12336 /**
12337  * @class Roo.DomTemplate
12338  * @extends Roo.Template
12339  * An effort at a dom based template engine..
12340  *
12341  * Similar to XTemplate, except it uses dom parsing to create the template..
12342  *
12343  * Supported features:
12344  *
12345  *  Tags:
12346
12347 <pre><code>
12348       {a_variable} - output encoded.
12349       {a_variable.format:("Y-m-d")} - call a method on the variable
12350       {a_variable:raw} - unencoded output
12351       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12352       {a_variable:this.method_on_template(...)} - call a method on the template object.
12353  
12354 </code></pre>
12355  *  The tpl tag:
12356 <pre><code>
12357         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12358         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12359         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12360         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12361   
12362 </code></pre>
12363  *      
12364  */
12365 Roo.DomTemplate = function()
12366 {
12367      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12368      if (this.html) {
12369         this.compile();
12370      }
12371 };
12372
12373
12374 Roo.extend(Roo.DomTemplate, Roo.Template, {
12375     /**
12376      * id counter for sub templates.
12377      */
12378     id : 0,
12379     /**
12380      * flag to indicate if dom parser is inside a pre,
12381      * it will strip whitespace if not.
12382      */
12383     inPre : false,
12384     
12385     /**
12386      * The various sub templates
12387      */
12388     tpls : false,
12389     
12390     
12391     
12392     /**
12393      *
12394      * basic tag replacing syntax
12395      * WORD:WORD()
12396      *
12397      * // you can fake an object call by doing this
12398      *  x.t:(test,tesT) 
12399      * 
12400      */
12401     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12402     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12403     
12404     iterChild : function (node, method) {
12405         
12406         var oldPre = this.inPre;
12407         if (node.tagName == 'PRE') {
12408             this.inPre = true;
12409         }
12410         for( var i = 0; i < node.childNodes.length; i++) {
12411             method.call(this, node.childNodes[i]);
12412         }
12413         this.inPre = oldPre;
12414     },
12415     
12416     
12417     
12418     /**
12419      * compile the template
12420      *
12421      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12422      *
12423      */
12424     compile: function()
12425     {
12426         var s = this.html;
12427         
12428         // covert the html into DOM...
12429         var doc = false;
12430         var div =false;
12431         try {
12432             doc = document.implementation.createHTMLDocument("");
12433             doc.documentElement.innerHTML =   this.html  ;
12434             div = doc.documentElement;
12435         } catch (e) {
12436             // old IE... - nasty -- it causes all sorts of issues.. with
12437             // images getting pulled from server..
12438             div = document.createElement('div');
12439             div.innerHTML = this.html;
12440         }
12441         //doc.documentElement.innerHTML = htmlBody
12442          
12443         
12444         
12445         this.tpls = [];
12446         var _t = this;
12447         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12448         
12449         var tpls = this.tpls;
12450         
12451         // create a top level template from the snippet..
12452         
12453         //Roo.log(div.innerHTML);
12454         
12455         var tpl = {
12456             uid : 'master',
12457             id : this.id++,
12458             attr : false,
12459             value : false,
12460             body : div.innerHTML,
12461             
12462             forCall : false,
12463             execCall : false,
12464             dom : div,
12465             isTop : true
12466             
12467         };
12468         tpls.unshift(tpl);
12469         
12470         
12471         // compile them...
12472         this.tpls = [];
12473         Roo.each(tpls, function(tp){
12474             this.compileTpl(tp);
12475             this.tpls[tp.id] = tp;
12476         }, this);
12477         
12478         this.master = tpls[0];
12479         return this;
12480         
12481         
12482     },
12483     
12484     compileNode : function(node, istop) {
12485         // test for
12486         //Roo.log(node);
12487         
12488         
12489         // skip anything not a tag..
12490         if (node.nodeType != 1) {
12491             if (node.nodeType == 3 && !this.inPre) {
12492                 // reduce white space..
12493                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12494                 
12495             }
12496             return;
12497         }
12498         
12499         var tpl = {
12500             uid : false,
12501             id : false,
12502             attr : false,
12503             value : false,
12504             body : '',
12505             
12506             forCall : false,
12507             execCall : false,
12508             dom : false,
12509             isTop : istop
12510             
12511             
12512         };
12513         
12514         
12515         switch(true) {
12516             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12517             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12518             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12519             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12520             // no default..
12521         }
12522         
12523         
12524         if (!tpl.attr) {
12525             // just itterate children..
12526             this.iterChild(node,this.compileNode);
12527             return;
12528         }
12529         tpl.uid = this.id++;
12530         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12531         node.removeAttribute('roo-'+ tpl.attr);
12532         if (tpl.attr != 'name') {
12533             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12534             node.parentNode.replaceChild(placeholder,  node);
12535         } else {
12536             
12537             var placeholder =  document.createElement('span');
12538             placeholder.className = 'roo-tpl-' + tpl.value;
12539             node.parentNode.replaceChild(placeholder,  node);
12540         }
12541         
12542         // parent now sees '{domtplXXXX}
12543         this.iterChild(node,this.compileNode);
12544         
12545         // we should now have node body...
12546         var div = document.createElement('div');
12547         div.appendChild(node);
12548         tpl.dom = node;
12549         // this has the unfortunate side effect of converting tagged attributes
12550         // eg. href="{...}" into %7C...%7D
12551         // this has been fixed by searching for those combo's although it's a bit hacky..
12552         
12553         
12554         tpl.body = div.innerHTML;
12555         
12556         
12557          
12558         tpl.id = tpl.uid;
12559         switch(tpl.attr) {
12560             case 'for' :
12561                 switch (tpl.value) {
12562                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12563                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12564                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12565                 }
12566                 break;
12567             
12568             case 'exec':
12569                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12570                 break;
12571             
12572             case 'if':     
12573                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12574                 break;
12575             
12576             case 'name':
12577                 tpl.id  = tpl.value; // replace non characters???
12578                 break;
12579             
12580         }
12581         
12582         
12583         this.tpls.push(tpl);
12584         
12585         
12586         
12587     },
12588     
12589     
12590     
12591     
12592     /**
12593      * Compile a segment of the template into a 'sub-template'
12594      *
12595      * 
12596      * 
12597      *
12598      */
12599     compileTpl : function(tpl)
12600     {
12601         var fm = Roo.util.Format;
12602         var useF = this.disableFormats !== true;
12603         
12604         var sep = Roo.isGecko ? "+\n" : ",\n";
12605         
12606         var undef = function(str) {
12607             Roo.debug && Roo.log("Property not found :"  + str);
12608             return '';
12609         };
12610           
12611         //Roo.log(tpl.body);
12612         
12613         
12614         
12615         var fn = function(m, lbrace, name, format, args)
12616         {
12617             //Roo.log("ARGS");
12618             //Roo.log(arguments);
12619             args = args ? args.replace(/\\'/g,"'") : args;
12620             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12621             if (typeof(format) == 'undefined') {
12622                 format =  'htmlEncode'; 
12623             }
12624             if (format == 'raw' ) {
12625                 format = false;
12626             }
12627             
12628             if(name.substr(0, 6) == 'domtpl'){
12629                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12630             }
12631             
12632             // build an array of options to determine if value is undefined..
12633             
12634             // basically get 'xxxx.yyyy' then do
12635             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12636             //    (function () { Roo.log("Property not found"); return ''; })() :
12637             //    ......
12638             
12639             var udef_ar = [];
12640             var lookfor = '';
12641             Roo.each(name.split('.'), function(st) {
12642                 lookfor += (lookfor.length ? '.': '') + st;
12643                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12644             });
12645             
12646             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12647             
12648             
12649             if(format && useF){
12650                 
12651                 args = args ? ',' + args : "";
12652                  
12653                 if(format.substr(0, 5) != "this."){
12654                     format = "fm." + format + '(';
12655                 }else{
12656                     format = 'this.call("'+ format.substr(5) + '", ';
12657                     args = ", values";
12658                 }
12659                 
12660                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12661             }
12662              
12663             if (args && args.length) {
12664                 // called with xxyx.yuu:(test,test)
12665                 // change to ()
12666                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12667             }
12668             // raw.. - :raw modifier..
12669             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12670             
12671         };
12672         var body;
12673         // branched to use + in gecko and [].join() in others
12674         if(Roo.isGecko){
12675             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12676                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12677                     "';};};";
12678         }else{
12679             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12680             body.push(tpl.body.replace(/(\r\n|\n)/g,
12681                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12682             body.push("'].join('');};};");
12683             body = body.join('');
12684         }
12685         
12686         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12687        
12688         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12689         eval(body);
12690         
12691         return this;
12692     },
12693      
12694     /**
12695      * same as applyTemplate, except it's done to one of the subTemplates
12696      * when using named templates, you can do:
12697      *
12698      * var str = pl.applySubTemplate('your-name', values);
12699      *
12700      * 
12701      * @param {Number} id of the template
12702      * @param {Object} values to apply to template
12703      * @param {Object} parent (normaly the instance of this object)
12704      */
12705     applySubTemplate : function(id, values, parent)
12706     {
12707         
12708         
12709         var t = this.tpls[id];
12710         
12711         
12712         try { 
12713             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12714                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12715                 return '';
12716             }
12717         } catch(e) {
12718             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12719             Roo.log(values);
12720           
12721             return '';
12722         }
12723         try { 
12724             
12725             if(t.execCall && t.execCall.call(this, values, parent)){
12726                 return '';
12727             }
12728         } catch(e) {
12729             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12730             Roo.log(values);
12731             return '';
12732         }
12733         
12734         try {
12735             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12736             parent = t.target ? values : parent;
12737             if(t.forCall && vs instanceof Array){
12738                 var buf = [];
12739                 for(var i = 0, len = vs.length; i < len; i++){
12740                     try {
12741                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12742                     } catch (e) {
12743                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12744                         Roo.log(e.body);
12745                         //Roo.log(t.compiled);
12746                         Roo.log(vs[i]);
12747                     }   
12748                 }
12749                 return buf.join('');
12750             }
12751         } catch (e) {
12752             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12753             Roo.log(values);
12754             return '';
12755         }
12756         try {
12757             return t.compiled.call(this, vs, parent);
12758         } catch (e) {
12759             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12760             Roo.log(e.body);
12761             //Roo.log(t.compiled);
12762             Roo.log(values);
12763             return '';
12764         }
12765     },
12766
12767    
12768
12769     applyTemplate : function(values){
12770         return this.master.compiled.call(this, values, {});
12771         //var s = this.subs;
12772     },
12773
12774     apply : function(){
12775         return this.applyTemplate.apply(this, arguments);
12776     }
12777
12778  });
12779
12780 Roo.DomTemplate.from = function(el){
12781     el = Roo.getDom(el);
12782     return new Roo.Domtemplate(el.value || el.innerHTML);
12783 };/*
12784  * Based on:
12785  * Ext JS Library 1.1.1
12786  * Copyright(c) 2006-2007, Ext JS, LLC.
12787  *
12788  * Originally Released Under LGPL - original licence link has changed is not relivant.
12789  *
12790  * Fork - LGPL
12791  * <script type="text/javascript">
12792  */
12793
12794 /**
12795  * @class Roo.util.DelayedTask
12796  * Provides a convenient method of performing setTimeout where a new
12797  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12798  * You can use this class to buffer
12799  * the keypress events for a certain number of milliseconds, and perform only if they stop
12800  * for that amount of time.
12801  * @constructor The parameters to this constructor serve as defaults and are not required.
12802  * @param {Function} fn (optional) The default function to timeout
12803  * @param {Object} scope (optional) The default scope of that timeout
12804  * @param {Array} args (optional) The default Array of arguments
12805  */
12806 Roo.util.DelayedTask = function(fn, scope, args){
12807     var id = null, d, t;
12808
12809     var call = function(){
12810         var now = new Date().getTime();
12811         if(now - t >= d){
12812             clearInterval(id);
12813             id = null;
12814             fn.apply(scope, args || []);
12815         }
12816     };
12817     /**
12818      * Cancels any pending timeout and queues a new one
12819      * @param {Number} delay The milliseconds to delay
12820      * @param {Function} newFn (optional) Overrides function passed to constructor
12821      * @param {Object} newScope (optional) Overrides scope passed to constructor
12822      * @param {Array} newArgs (optional) Overrides args passed to constructor
12823      */
12824     this.delay = function(delay, newFn, newScope, newArgs){
12825         if(id && delay != d){
12826             this.cancel();
12827         }
12828         d = delay;
12829         t = new Date().getTime();
12830         fn = newFn || fn;
12831         scope = newScope || scope;
12832         args = newArgs || args;
12833         if(!id){
12834             id = setInterval(call, d);
12835         }
12836     };
12837
12838     /**
12839      * Cancel the last queued timeout
12840      */
12841     this.cancel = function(){
12842         if(id){
12843             clearInterval(id);
12844             id = null;
12845         }
12846     };
12847 };/*
12848  * Based on:
12849  * Ext JS Library 1.1.1
12850  * Copyright(c) 2006-2007, Ext JS, LLC.
12851  *
12852  * Originally Released Under LGPL - original licence link has changed is not relivant.
12853  *
12854  * Fork - LGPL
12855  * <script type="text/javascript">
12856  */
12857  
12858  
12859 Roo.util.TaskRunner = function(interval){
12860     interval = interval || 10;
12861     var tasks = [], removeQueue = [];
12862     var id = 0;
12863     var running = false;
12864
12865     var stopThread = function(){
12866         running = false;
12867         clearInterval(id);
12868         id = 0;
12869     };
12870
12871     var startThread = function(){
12872         if(!running){
12873             running = true;
12874             id = setInterval(runTasks, interval);
12875         }
12876     };
12877
12878     var removeTask = function(task){
12879         removeQueue.push(task);
12880         if(task.onStop){
12881             task.onStop();
12882         }
12883     };
12884
12885     var runTasks = function(){
12886         if(removeQueue.length > 0){
12887             for(var i = 0, len = removeQueue.length; i < len; i++){
12888                 tasks.remove(removeQueue[i]);
12889             }
12890             removeQueue = [];
12891             if(tasks.length < 1){
12892                 stopThread();
12893                 return;
12894             }
12895         }
12896         var now = new Date().getTime();
12897         for(var i = 0, len = tasks.length; i < len; ++i){
12898             var t = tasks[i];
12899             var itime = now - t.taskRunTime;
12900             if(t.interval <= itime){
12901                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12902                 t.taskRunTime = now;
12903                 if(rt === false || t.taskRunCount === t.repeat){
12904                     removeTask(t);
12905                     return;
12906                 }
12907             }
12908             if(t.duration && t.duration <= (now - t.taskStartTime)){
12909                 removeTask(t);
12910             }
12911         }
12912     };
12913
12914     /**
12915      * Queues a new task.
12916      * @param {Object} task
12917      */
12918     this.start = function(task){
12919         tasks.push(task);
12920         task.taskStartTime = new Date().getTime();
12921         task.taskRunTime = 0;
12922         task.taskRunCount = 0;
12923         startThread();
12924         return task;
12925     };
12926
12927     this.stop = function(task){
12928         removeTask(task);
12929         return task;
12930     };
12931
12932     this.stopAll = function(){
12933         stopThread();
12934         for(var i = 0, len = tasks.length; i < len; i++){
12935             if(tasks[i].onStop){
12936                 tasks[i].onStop();
12937             }
12938         }
12939         tasks = [];
12940         removeQueue = [];
12941     };
12942 };
12943
12944 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12945  * Based on:
12946  * Ext JS Library 1.1.1
12947  * Copyright(c) 2006-2007, Ext JS, LLC.
12948  *
12949  * Originally Released Under LGPL - original licence link has changed is not relivant.
12950  *
12951  * Fork - LGPL
12952  * <script type="text/javascript">
12953  */
12954
12955  
12956 /**
12957  * @class Roo.util.MixedCollection
12958  * @extends Roo.util.Observable
12959  * A Collection class that maintains both numeric indexes and keys and exposes events.
12960  * @constructor
12961  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12962  * collection (defaults to false)
12963  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12964  * and return the key value for that item.  This is used when available to look up the key on items that
12965  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12966  * equivalent to providing an implementation for the {@link #getKey} method.
12967  */
12968 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12969     this.items = [];
12970     this.map = {};
12971     this.keys = [];
12972     this.length = 0;
12973     this.addEvents({
12974         /**
12975          * @event clear
12976          * Fires when the collection is cleared.
12977          */
12978         "clear" : true,
12979         /**
12980          * @event add
12981          * Fires when an item is added to the collection.
12982          * @param {Number} index The index at which the item was added.
12983          * @param {Object} o The item added.
12984          * @param {String} key The key associated with the added item.
12985          */
12986         "add" : true,
12987         /**
12988          * @event replace
12989          * Fires when an item is replaced in the collection.
12990          * @param {String} key he key associated with the new added.
12991          * @param {Object} old The item being replaced.
12992          * @param {Object} new The new item.
12993          */
12994         "replace" : true,
12995         /**
12996          * @event remove
12997          * Fires when an item is removed from the collection.
12998          * @param {Object} o The item being removed.
12999          * @param {String} key (optional) The key associated with the removed item.
13000          */
13001         "remove" : true,
13002         "sort" : true
13003     });
13004     this.allowFunctions = allowFunctions === true;
13005     if(keyFn){
13006         this.getKey = keyFn;
13007     }
13008     Roo.util.MixedCollection.superclass.constructor.call(this);
13009 };
13010
13011 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13012     allowFunctions : false,
13013     
13014 /**
13015  * Adds an item to the collection.
13016  * @param {String} key The key to associate with the item
13017  * @param {Object} o The item to add.
13018  * @return {Object} The item added.
13019  */
13020     add : function(key, o){
13021         if(arguments.length == 1){
13022             o = arguments[0];
13023             key = this.getKey(o);
13024         }
13025         if(typeof key == "undefined" || key === null){
13026             this.length++;
13027             this.items.push(o);
13028             this.keys.push(null);
13029         }else{
13030             var old = this.map[key];
13031             if(old){
13032                 return this.replace(key, o);
13033             }
13034             this.length++;
13035             this.items.push(o);
13036             this.map[key] = o;
13037             this.keys.push(key);
13038         }
13039         this.fireEvent("add", this.length-1, o, key);
13040         return o;
13041     },
13042        
13043 /**
13044   * MixedCollection has a generic way to fetch keys if you implement getKey.
13045 <pre><code>
13046 // normal way
13047 var mc = new Roo.util.MixedCollection();
13048 mc.add(someEl.dom.id, someEl);
13049 mc.add(otherEl.dom.id, otherEl);
13050 //and so on
13051
13052 // using getKey
13053 var mc = new Roo.util.MixedCollection();
13054 mc.getKey = function(el){
13055    return el.dom.id;
13056 };
13057 mc.add(someEl);
13058 mc.add(otherEl);
13059
13060 // or via the constructor
13061 var mc = new Roo.util.MixedCollection(false, function(el){
13062    return el.dom.id;
13063 });
13064 mc.add(someEl);
13065 mc.add(otherEl);
13066 </code></pre>
13067  * @param o {Object} The item for which to find the key.
13068  * @return {Object} The key for the passed item.
13069  */
13070     getKey : function(o){
13071          return o.id; 
13072     },
13073    
13074 /**
13075  * Replaces an item in the collection.
13076  * @param {String} key The key associated with the item to replace, or the item to replace.
13077  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13078  * @return {Object}  The new item.
13079  */
13080     replace : function(key, o){
13081         if(arguments.length == 1){
13082             o = arguments[0];
13083             key = this.getKey(o);
13084         }
13085         var old = this.item(key);
13086         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13087              return this.add(key, o);
13088         }
13089         var index = this.indexOfKey(key);
13090         this.items[index] = o;
13091         this.map[key] = o;
13092         this.fireEvent("replace", key, old, o);
13093         return o;
13094     },
13095    
13096 /**
13097  * Adds all elements of an Array or an Object to the collection.
13098  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13099  * an Array of values, each of which are added to the collection.
13100  */
13101     addAll : function(objs){
13102         if(arguments.length > 1 || objs instanceof Array){
13103             var args = arguments.length > 1 ? arguments : objs;
13104             for(var i = 0, len = args.length; i < len; i++){
13105                 this.add(args[i]);
13106             }
13107         }else{
13108             for(var key in objs){
13109                 if(this.allowFunctions || typeof objs[key] != "function"){
13110                     this.add(key, objs[key]);
13111                 }
13112             }
13113         }
13114     },
13115    
13116 /**
13117  * Executes the specified function once for every item in the collection, passing each
13118  * item as the first and only parameter. returning false from the function will stop the iteration.
13119  * @param {Function} fn The function to execute for each item.
13120  * @param {Object} scope (optional) The scope in which to execute the function.
13121  */
13122     each : function(fn, scope){
13123         var items = [].concat(this.items); // each safe for removal
13124         for(var i = 0, len = items.length; i < len; i++){
13125             if(fn.call(scope || items[i], items[i], i, len) === false){
13126                 break;
13127             }
13128         }
13129     },
13130    
13131 /**
13132  * Executes the specified function once for every key in the collection, passing each
13133  * key, and its associated item as the first two parameters.
13134  * @param {Function} fn The function to execute for each item.
13135  * @param {Object} scope (optional) The scope in which to execute the function.
13136  */
13137     eachKey : function(fn, scope){
13138         for(var i = 0, len = this.keys.length; i < len; i++){
13139             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13140         }
13141     },
13142    
13143 /**
13144  * Returns the first item in the collection which elicits a true return value from the
13145  * passed selection function.
13146  * @param {Function} fn The selection function to execute for each item.
13147  * @param {Object} scope (optional) The scope in which to execute the function.
13148  * @return {Object} The first item in the collection which returned true from the selection function.
13149  */
13150     find : function(fn, scope){
13151         for(var i = 0, len = this.items.length; i < len; i++){
13152             if(fn.call(scope || window, this.items[i], this.keys[i])){
13153                 return this.items[i];
13154             }
13155         }
13156         return null;
13157     },
13158    
13159 /**
13160  * Inserts an item at the specified index in the collection.
13161  * @param {Number} index The index to insert the item at.
13162  * @param {String} key The key to associate with the new item, or the item itself.
13163  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13164  * @return {Object} The item inserted.
13165  */
13166     insert : function(index, key, o){
13167         if(arguments.length == 2){
13168             o = arguments[1];
13169             key = this.getKey(o);
13170         }
13171         if(index >= this.length){
13172             return this.add(key, o);
13173         }
13174         this.length++;
13175         this.items.splice(index, 0, o);
13176         if(typeof key != "undefined" && key != null){
13177             this.map[key] = o;
13178         }
13179         this.keys.splice(index, 0, key);
13180         this.fireEvent("add", index, o, key);
13181         return o;
13182     },
13183    
13184 /**
13185  * Removed an item from the collection.
13186  * @param {Object} o The item to remove.
13187  * @return {Object} The item removed.
13188  */
13189     remove : function(o){
13190         return this.removeAt(this.indexOf(o));
13191     },
13192    
13193 /**
13194  * Remove an item from a specified index in the collection.
13195  * @param {Number} index The index within the collection of the item to remove.
13196  */
13197     removeAt : function(index){
13198         if(index < this.length && index >= 0){
13199             this.length--;
13200             var o = this.items[index];
13201             this.items.splice(index, 1);
13202             var key = this.keys[index];
13203             if(typeof key != "undefined"){
13204                 delete this.map[key];
13205             }
13206             this.keys.splice(index, 1);
13207             this.fireEvent("remove", o, key);
13208         }
13209     },
13210    
13211 /**
13212  * Removed an item associated with the passed key fom the collection.
13213  * @param {String} key The key of the item to remove.
13214  */
13215     removeKey : function(key){
13216         return this.removeAt(this.indexOfKey(key));
13217     },
13218    
13219 /**
13220  * Returns the number of items in the collection.
13221  * @return {Number} the number of items in the collection.
13222  */
13223     getCount : function(){
13224         return this.length; 
13225     },
13226    
13227 /**
13228  * Returns index within the collection of the passed Object.
13229  * @param {Object} o The item to find the index of.
13230  * @return {Number} index of the item.
13231  */
13232     indexOf : function(o){
13233         if(!this.items.indexOf){
13234             for(var i = 0, len = this.items.length; i < len; i++){
13235                 if(this.items[i] == o) {
13236                     return i;
13237                 }
13238             }
13239             return -1;
13240         }else{
13241             return this.items.indexOf(o);
13242         }
13243     },
13244    
13245 /**
13246  * Returns index within the collection of the passed key.
13247  * @param {String} key The key to find the index of.
13248  * @return {Number} index of the key.
13249  */
13250     indexOfKey : function(key){
13251         if(!this.keys.indexOf){
13252             for(var i = 0, len = this.keys.length; i < len; i++){
13253                 if(this.keys[i] == key) {
13254                     return i;
13255                 }
13256             }
13257             return -1;
13258         }else{
13259             return this.keys.indexOf(key);
13260         }
13261     },
13262    
13263 /**
13264  * Returns the item associated with the passed key OR index. Key has priority over index.
13265  * @param {String/Number} key The key or index of the item.
13266  * @return {Object} The item associated with the passed key.
13267  */
13268     item : function(key){
13269         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13270         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13271     },
13272     
13273 /**
13274  * Returns the item at the specified index.
13275  * @param {Number} index The index of the item.
13276  * @return {Object}
13277  */
13278     itemAt : function(index){
13279         return this.items[index];
13280     },
13281     
13282 /**
13283  * Returns the item associated with the passed key.
13284  * @param {String/Number} key The key of the item.
13285  * @return {Object} The item associated with the passed key.
13286  */
13287     key : function(key){
13288         return this.map[key];
13289     },
13290    
13291 /**
13292  * Returns true if the collection contains the passed Object as an item.
13293  * @param {Object} o  The Object to look for in the collection.
13294  * @return {Boolean} True if the collection contains the Object as an item.
13295  */
13296     contains : function(o){
13297         return this.indexOf(o) != -1;
13298     },
13299    
13300 /**
13301  * Returns true if the collection contains the passed Object as a key.
13302  * @param {String} key The key to look for in the collection.
13303  * @return {Boolean} True if the collection contains the Object as a key.
13304  */
13305     containsKey : function(key){
13306         return typeof this.map[key] != "undefined";
13307     },
13308    
13309 /**
13310  * Removes all items from the collection.
13311  */
13312     clear : function(){
13313         this.length = 0;
13314         this.items = [];
13315         this.keys = [];
13316         this.map = {};
13317         this.fireEvent("clear");
13318     },
13319    
13320 /**
13321  * Returns the first item in the collection.
13322  * @return {Object} the first item in the collection..
13323  */
13324     first : function(){
13325         return this.items[0]; 
13326     },
13327    
13328 /**
13329  * Returns the last item in the collection.
13330  * @return {Object} the last item in the collection..
13331  */
13332     last : function(){
13333         return this.items[this.length-1];   
13334     },
13335     
13336     _sort : function(property, dir, fn){
13337         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13338         fn = fn || function(a, b){
13339             return a-b;
13340         };
13341         var c = [], k = this.keys, items = this.items;
13342         for(var i = 0, len = items.length; i < len; i++){
13343             c[c.length] = {key: k[i], value: items[i], index: i};
13344         }
13345         c.sort(function(a, b){
13346             var v = fn(a[property], b[property]) * dsc;
13347             if(v == 0){
13348                 v = (a.index < b.index ? -1 : 1);
13349             }
13350             return v;
13351         });
13352         for(var i = 0, len = c.length; i < len; i++){
13353             items[i] = c[i].value;
13354             k[i] = c[i].key;
13355         }
13356         this.fireEvent("sort", this);
13357     },
13358     
13359     /**
13360      * Sorts this collection with the passed comparison function
13361      * @param {String} direction (optional) "ASC" or "DESC"
13362      * @param {Function} fn (optional) comparison function
13363      */
13364     sort : function(dir, fn){
13365         this._sort("value", dir, fn);
13366     },
13367     
13368     /**
13369      * Sorts this collection by keys
13370      * @param {String} direction (optional) "ASC" or "DESC"
13371      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13372      */
13373     keySort : function(dir, fn){
13374         this._sort("key", dir, fn || function(a, b){
13375             return String(a).toUpperCase()-String(b).toUpperCase();
13376         });
13377     },
13378     
13379     /**
13380      * Returns a range of items in this collection
13381      * @param {Number} startIndex (optional) defaults to 0
13382      * @param {Number} endIndex (optional) default to the last item
13383      * @return {Array} An array of items
13384      */
13385     getRange : function(start, end){
13386         var items = this.items;
13387         if(items.length < 1){
13388             return [];
13389         }
13390         start = start || 0;
13391         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13392         var r = [];
13393         if(start <= end){
13394             for(var i = start; i <= end; i++) {
13395                     r[r.length] = items[i];
13396             }
13397         }else{
13398             for(var i = start; i >= end; i--) {
13399                     r[r.length] = items[i];
13400             }
13401         }
13402         return r;
13403     },
13404         
13405     /**
13406      * Filter the <i>objects</i> in this collection by a specific property. 
13407      * Returns a new collection that has been filtered.
13408      * @param {String} property A property on your objects
13409      * @param {String/RegExp} value Either string that the property values 
13410      * should start with or a RegExp to test against the property
13411      * @return {MixedCollection} The new filtered collection
13412      */
13413     filter : function(property, value){
13414         if(!value.exec){ // not a regex
13415             value = String(value);
13416             if(value.length == 0){
13417                 return this.clone();
13418             }
13419             value = new RegExp("^" + Roo.escapeRe(value), "i");
13420         }
13421         return this.filterBy(function(o){
13422             return o && value.test(o[property]);
13423         });
13424         },
13425     
13426     /**
13427      * Filter by a function. * Returns a new collection that has been filtered.
13428      * The passed function will be called with each 
13429      * object in the collection. If the function returns true, the value is included 
13430      * otherwise it is filtered.
13431      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13432      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13433      * @return {MixedCollection} The new filtered collection
13434      */
13435     filterBy : function(fn, scope){
13436         var r = new Roo.util.MixedCollection();
13437         r.getKey = this.getKey;
13438         var k = this.keys, it = this.items;
13439         for(var i = 0, len = it.length; i < len; i++){
13440             if(fn.call(scope||this, it[i], k[i])){
13441                                 r.add(k[i], it[i]);
13442                         }
13443         }
13444         return r;
13445     },
13446     
13447     /**
13448      * Creates a duplicate of this collection
13449      * @return {MixedCollection}
13450      */
13451     clone : function(){
13452         var r = new Roo.util.MixedCollection();
13453         var k = this.keys, it = this.items;
13454         for(var i = 0, len = it.length; i < len; i++){
13455             r.add(k[i], it[i]);
13456         }
13457         r.getKey = this.getKey;
13458         return r;
13459     }
13460 });
13461 /**
13462  * Returns the item associated with the passed key or index.
13463  * @method
13464  * @param {String/Number} key The key or index of the item.
13465  * @return {Object} The item associated with the passed key.
13466  */
13467 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13468  * Based on:
13469  * Ext JS Library 1.1.1
13470  * Copyright(c) 2006-2007, Ext JS, LLC.
13471  *
13472  * Originally Released Under LGPL - original licence link has changed is not relivant.
13473  *
13474  * Fork - LGPL
13475  * <script type="text/javascript">
13476  */
13477 /**
13478  * @class Roo.util.JSON
13479  * Modified version of Douglas Crockford"s json.js that doesn"t
13480  * mess with the Object prototype 
13481  * http://www.json.org/js.html
13482  * @singleton
13483  */
13484 Roo.util.JSON = new (function(){
13485     var useHasOwn = {}.hasOwnProperty ? true : false;
13486     
13487     // crashes Safari in some instances
13488     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13489     
13490     var pad = function(n) {
13491         return n < 10 ? "0" + n : n;
13492     };
13493     
13494     var m = {
13495         "\b": '\\b',
13496         "\t": '\\t',
13497         "\n": '\\n',
13498         "\f": '\\f',
13499         "\r": '\\r',
13500         '"' : '\\"',
13501         "\\": '\\\\'
13502     };
13503
13504     var encodeString = function(s){
13505         if (/["\\\x00-\x1f]/.test(s)) {
13506             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13507                 var c = m[b];
13508                 if(c){
13509                     return c;
13510                 }
13511                 c = b.charCodeAt();
13512                 return "\\u00" +
13513                     Math.floor(c / 16).toString(16) +
13514                     (c % 16).toString(16);
13515             }) + '"';
13516         }
13517         return '"' + s + '"';
13518     };
13519     
13520     var encodeArray = function(o){
13521         var a = ["["], b, i, l = o.length, v;
13522             for (i = 0; i < l; i += 1) {
13523                 v = o[i];
13524                 switch (typeof v) {
13525                     case "undefined":
13526                     case "function":
13527                     case "unknown":
13528                         break;
13529                     default:
13530                         if (b) {
13531                             a.push(',');
13532                         }
13533                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13534                         b = true;
13535                 }
13536             }
13537             a.push("]");
13538             return a.join("");
13539     };
13540     
13541     var encodeDate = function(o){
13542         return '"' + o.getFullYear() + "-" +
13543                 pad(o.getMonth() + 1) + "-" +
13544                 pad(o.getDate()) + "T" +
13545                 pad(o.getHours()) + ":" +
13546                 pad(o.getMinutes()) + ":" +
13547                 pad(o.getSeconds()) + '"';
13548     };
13549     
13550     /**
13551      * Encodes an Object, Array or other value
13552      * @param {Mixed} o The variable to encode
13553      * @return {String} The JSON string
13554      */
13555     this.encode = function(o)
13556     {
13557         // should this be extended to fully wrap stringify..
13558         
13559         if(typeof o == "undefined" || o === null){
13560             return "null";
13561         }else if(o instanceof Array){
13562             return encodeArray(o);
13563         }else if(o instanceof Date){
13564             return encodeDate(o);
13565         }else if(typeof o == "string"){
13566             return encodeString(o);
13567         }else if(typeof o == "number"){
13568             return isFinite(o) ? String(o) : "null";
13569         }else if(typeof o == "boolean"){
13570             return String(o);
13571         }else {
13572             var a = ["{"], b, i, v;
13573             for (i in o) {
13574                 if(!useHasOwn || o.hasOwnProperty(i)) {
13575                     v = o[i];
13576                     switch (typeof v) {
13577                     case "undefined":
13578                     case "function":
13579                     case "unknown":
13580                         break;
13581                     default:
13582                         if(b){
13583                             a.push(',');
13584                         }
13585                         a.push(this.encode(i), ":",
13586                                 v === null ? "null" : this.encode(v));
13587                         b = true;
13588                     }
13589                 }
13590             }
13591             a.push("}");
13592             return a.join("");
13593         }
13594     };
13595     
13596     /**
13597      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13598      * @param {String} json The JSON string
13599      * @return {Object} The resulting object
13600      */
13601     this.decode = function(json){
13602         
13603         return  /** eval:var:json */ eval("(" + json + ')');
13604     };
13605 })();
13606 /** 
13607  * Shorthand for {@link Roo.util.JSON#encode}
13608  * @member Roo encode 
13609  * @method */
13610 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13611 /** 
13612  * Shorthand for {@link Roo.util.JSON#decode}
13613  * @member Roo decode 
13614  * @method */
13615 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13616 /*
13617  * Based on:
13618  * Ext JS Library 1.1.1
13619  * Copyright(c) 2006-2007, Ext JS, LLC.
13620  *
13621  * Originally Released Under LGPL - original licence link has changed is not relivant.
13622  *
13623  * Fork - LGPL
13624  * <script type="text/javascript">
13625  */
13626  
13627 /**
13628  * @class Roo.util.Format
13629  * Reusable data formatting functions
13630  * @singleton
13631  */
13632 Roo.util.Format = function(){
13633     var trimRe = /^\s+|\s+$/g;
13634     return {
13635         /**
13636          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13637          * @param {String} value The string to truncate
13638          * @param {Number} length The maximum length to allow before truncating
13639          * @return {String} The converted text
13640          */
13641         ellipsis : function(value, len){
13642             if(value && value.length > len){
13643                 return value.substr(0, len-3)+"...";
13644             }
13645             return value;
13646         },
13647
13648         /**
13649          * Checks a reference and converts it to empty string if it is undefined
13650          * @param {Mixed} value Reference to check
13651          * @return {Mixed} Empty string if converted, otherwise the original value
13652          */
13653         undef : function(value){
13654             return typeof value != "undefined" ? value : "";
13655         },
13656
13657         /**
13658          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13659          * @param {String} value The string to encode
13660          * @return {String} The encoded text
13661          */
13662         htmlEncode : function(value){
13663             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13664         },
13665
13666         /**
13667          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13668          * @param {String} value The string to decode
13669          * @return {String} The decoded text
13670          */
13671         htmlDecode : function(value){
13672             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13673         },
13674
13675         /**
13676          * Trims any whitespace from either side of a string
13677          * @param {String} value The text to trim
13678          * @return {String} The trimmed text
13679          */
13680         trim : function(value){
13681             return String(value).replace(trimRe, "");
13682         },
13683
13684         /**
13685          * Returns a substring from within an original string
13686          * @param {String} value The original text
13687          * @param {Number} start The start index of the substring
13688          * @param {Number} length The length of the substring
13689          * @return {String} The substring
13690          */
13691         substr : function(value, start, length){
13692             return String(value).substr(start, length);
13693         },
13694
13695         /**
13696          * Converts a string to all lower case letters
13697          * @param {String} value The text to convert
13698          * @return {String} The converted text
13699          */
13700         lowercase : function(value){
13701             return String(value).toLowerCase();
13702         },
13703
13704         /**
13705          * Converts a string to all upper case letters
13706          * @param {String} value The text to convert
13707          * @return {String} The converted text
13708          */
13709         uppercase : function(value){
13710             return String(value).toUpperCase();
13711         },
13712
13713         /**
13714          * Converts the first character only of a string to upper case
13715          * @param {String} value The text to convert
13716          * @return {String} The converted text
13717          */
13718         capitalize : function(value){
13719             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13720         },
13721
13722         // private
13723         call : function(value, fn){
13724             if(arguments.length > 2){
13725                 var args = Array.prototype.slice.call(arguments, 2);
13726                 args.unshift(value);
13727                  
13728                 return /** eval:var:value */  eval(fn).apply(window, args);
13729             }else{
13730                 /** eval:var:value */
13731                 return /** eval:var:value */ eval(fn).call(window, value);
13732             }
13733         },
13734
13735        
13736         /**
13737          * safer version of Math.toFixed..??/
13738          * @param {Number/String} value The numeric value to format
13739          * @param {Number/String} value Decimal places 
13740          * @return {String} The formatted currency string
13741          */
13742         toFixed : function(v, n)
13743         {
13744             // why not use to fixed - precision is buggered???
13745             if (!n) {
13746                 return Math.round(v-0);
13747             }
13748             var fact = Math.pow(10,n+1);
13749             v = (Math.round((v-0)*fact))/fact;
13750             var z = (''+fact).substring(2);
13751             if (v == Math.floor(v)) {
13752                 return Math.floor(v) + '.' + z;
13753             }
13754             
13755             // now just padd decimals..
13756             var ps = String(v).split('.');
13757             var fd = (ps[1] + z);
13758             var r = fd.substring(0,n); 
13759             var rm = fd.substring(n); 
13760             if (rm < 5) {
13761                 return ps[0] + '.' + r;
13762             }
13763             r*=1; // turn it into a number;
13764             r++;
13765             if (String(r).length != n) {
13766                 ps[0]*=1;
13767                 ps[0]++;
13768                 r = String(r).substring(1); // chop the end off.
13769             }
13770             
13771             return ps[0] + '.' + r;
13772              
13773         },
13774         
13775         /**
13776          * Format a number as US currency
13777          * @param {Number/String} value The numeric value to format
13778          * @return {String} The formatted currency string
13779          */
13780         usMoney : function(v){
13781             return '$' + Roo.util.Format.number(v);
13782         },
13783         
13784         /**
13785          * Format a number
13786          * eventually this should probably emulate php's number_format
13787          * @param {Number/String} value The numeric value to format
13788          * @param {Number} decimals number of decimal places
13789          * @param {String} delimiter for thousands (default comma)
13790          * @return {String} The formatted currency string
13791          */
13792         number : function(v, decimals, thousandsDelimiter)
13793         {
13794             // multiply and round.
13795             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13796             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
13797             
13798             var mul = Math.pow(10, decimals);
13799             var zero = String(mul).substring(1);
13800             v = (Math.round((v-0)*mul))/mul;
13801             
13802             // if it's '0' number.. then
13803             
13804             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13805             v = String(v);
13806             var ps = v.split('.');
13807             var whole = ps[0];
13808             
13809             var r = /(\d+)(\d{3})/;
13810             // add comma's
13811             
13812             if(thousandsDelimiter.length != 0) {
13813                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
13814             } 
13815             
13816             var sub = ps[1] ?
13817                     // has decimals..
13818                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13819                     // does not have decimals
13820                     (decimals ? ('.' + zero) : '');
13821             
13822             
13823             return whole + sub ;
13824         },
13825         
13826         /**
13827          * Parse a value into a formatted date using the specified format pattern.
13828          * @param {Mixed} value The value to format
13829          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13830          * @return {String} The formatted date string
13831          */
13832         date : function(v, format){
13833             if(!v){
13834                 return "";
13835             }
13836             if(!(v instanceof Date)){
13837                 v = new Date(Date.parse(v));
13838             }
13839             return v.dateFormat(format || Roo.util.Format.defaults.date);
13840         },
13841
13842         /**
13843          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13844          * @param {String} format Any valid date format string
13845          * @return {Function} The date formatting function
13846          */
13847         dateRenderer : function(format){
13848             return function(v){
13849                 return Roo.util.Format.date(v, format);  
13850             };
13851         },
13852
13853         // private
13854         stripTagsRE : /<\/?[^>]+>/gi,
13855         
13856         /**
13857          * Strips all HTML tags
13858          * @param {Mixed} value The text from which to strip tags
13859          * @return {String} The stripped text
13860          */
13861         stripTags : function(v){
13862             return !v ? v : String(v).replace(this.stripTagsRE, "");
13863         }
13864     };
13865 }();
13866 Roo.util.Format.defaults = {
13867     date : 'd/M/Y'
13868 };/*
13869  * Based on:
13870  * Ext JS Library 1.1.1
13871  * Copyright(c) 2006-2007, Ext JS, LLC.
13872  *
13873  * Originally Released Under LGPL - original licence link has changed is not relivant.
13874  *
13875  * Fork - LGPL
13876  * <script type="text/javascript">
13877  */
13878
13879
13880  
13881
13882 /**
13883  * @class Roo.MasterTemplate
13884  * @extends Roo.Template
13885  * Provides a template that can have child templates. The syntax is:
13886 <pre><code>
13887 var t = new Roo.MasterTemplate(
13888         '&lt;select name="{name}"&gt;',
13889                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13890         '&lt;/select&gt;'
13891 );
13892 t.add('options', {value: 'foo', text: 'bar'});
13893 // or you can add multiple child elements in one shot
13894 t.addAll('options', [
13895     {value: 'foo', text: 'bar'},
13896     {value: 'foo2', text: 'bar2'},
13897     {value: 'foo3', text: 'bar3'}
13898 ]);
13899 // then append, applying the master template values
13900 t.append('my-form', {name: 'my-select'});
13901 </code></pre>
13902 * A name attribute for the child template is not required if you have only one child
13903 * template or you want to refer to them by index.
13904  */
13905 Roo.MasterTemplate = function(){
13906     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13907     this.originalHtml = this.html;
13908     var st = {};
13909     var m, re = this.subTemplateRe;
13910     re.lastIndex = 0;
13911     var subIndex = 0;
13912     while(m = re.exec(this.html)){
13913         var name = m[1], content = m[2];
13914         st[subIndex] = {
13915             name: name,
13916             index: subIndex,
13917             buffer: [],
13918             tpl : new Roo.Template(content)
13919         };
13920         if(name){
13921             st[name] = st[subIndex];
13922         }
13923         st[subIndex].tpl.compile();
13924         st[subIndex].tpl.call = this.call.createDelegate(this);
13925         subIndex++;
13926     }
13927     this.subCount = subIndex;
13928     this.subs = st;
13929 };
13930 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13931     /**
13932     * The regular expression used to match sub templates
13933     * @type RegExp
13934     * @property
13935     */
13936     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13937
13938     /**
13939      * Applies the passed values to a child template.
13940      * @param {String/Number} name (optional) The name or index of the child template
13941      * @param {Array/Object} values The values to be applied to the template
13942      * @return {MasterTemplate} this
13943      */
13944      add : function(name, values){
13945         if(arguments.length == 1){
13946             values = arguments[0];
13947             name = 0;
13948         }
13949         var s = this.subs[name];
13950         s.buffer[s.buffer.length] = s.tpl.apply(values);
13951         return this;
13952     },
13953
13954     /**
13955      * Applies all the passed values to a child template.
13956      * @param {String/Number} name (optional) The name or index of the child template
13957      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13958      * @param {Boolean} reset (optional) True to reset the template first
13959      * @return {MasterTemplate} this
13960      */
13961     fill : function(name, values, reset){
13962         var a = arguments;
13963         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13964             values = a[0];
13965             name = 0;
13966             reset = a[1];
13967         }
13968         if(reset){
13969             this.reset();
13970         }
13971         for(var i = 0, len = values.length; i < len; i++){
13972             this.add(name, values[i]);
13973         }
13974         return this;
13975     },
13976
13977     /**
13978      * Resets the template for reuse
13979      * @return {MasterTemplate} this
13980      */
13981      reset : function(){
13982         var s = this.subs;
13983         for(var i = 0; i < this.subCount; i++){
13984             s[i].buffer = [];
13985         }
13986         return this;
13987     },
13988
13989     applyTemplate : function(values){
13990         var s = this.subs;
13991         var replaceIndex = -1;
13992         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13993             return s[++replaceIndex].buffer.join("");
13994         });
13995         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13996     },
13997
13998     apply : function(){
13999         return this.applyTemplate.apply(this, arguments);
14000     },
14001
14002     compile : function(){return this;}
14003 });
14004
14005 /**
14006  * Alias for fill().
14007  * @method
14008  */
14009 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14010  /**
14011  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14012  * var tpl = Roo.MasterTemplate.from('element-id');
14013  * @param {String/HTMLElement} el
14014  * @param {Object} config
14015  * @static
14016  */
14017 Roo.MasterTemplate.from = function(el, config){
14018     el = Roo.getDom(el);
14019     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14020 };/*
14021  * Based on:
14022  * Ext JS Library 1.1.1
14023  * Copyright(c) 2006-2007, Ext JS, LLC.
14024  *
14025  * Originally Released Under LGPL - original licence link has changed is not relivant.
14026  *
14027  * Fork - LGPL
14028  * <script type="text/javascript">
14029  */
14030
14031  
14032 /**
14033  * @class Roo.util.CSS
14034  * Utility class for manipulating CSS rules
14035  * @singleton
14036  */
14037 Roo.util.CSS = function(){
14038         var rules = null;
14039         var doc = document;
14040
14041     var camelRe = /(-[a-z])/gi;
14042     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14043
14044    return {
14045    /**
14046     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14047     * tag and appended to the HEAD of the document.
14048     * @param {String|Object} cssText The text containing the css rules
14049     * @param {String} id An id to add to the stylesheet for later removal
14050     * @return {StyleSheet}
14051     */
14052     createStyleSheet : function(cssText, id){
14053         var ss;
14054         var head = doc.getElementsByTagName("head")[0];
14055         var nrules = doc.createElement("style");
14056         nrules.setAttribute("type", "text/css");
14057         if(id){
14058             nrules.setAttribute("id", id);
14059         }
14060         if (typeof(cssText) != 'string') {
14061             // support object maps..
14062             // not sure if this a good idea.. 
14063             // perhaps it should be merged with the general css handling
14064             // and handle js style props.
14065             var cssTextNew = [];
14066             for(var n in cssText) {
14067                 var citems = [];
14068                 for(var k in cssText[n]) {
14069                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14070                 }
14071                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14072                 
14073             }
14074             cssText = cssTextNew.join("\n");
14075             
14076         }
14077        
14078        
14079        if(Roo.isIE){
14080            head.appendChild(nrules);
14081            ss = nrules.styleSheet;
14082            ss.cssText = cssText;
14083        }else{
14084            try{
14085                 nrules.appendChild(doc.createTextNode(cssText));
14086            }catch(e){
14087                nrules.cssText = cssText; 
14088            }
14089            head.appendChild(nrules);
14090            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14091        }
14092        this.cacheStyleSheet(ss);
14093        return ss;
14094    },
14095
14096    /**
14097     * Removes a style or link tag by id
14098     * @param {String} id The id of the tag
14099     */
14100    removeStyleSheet : function(id){
14101        var existing = doc.getElementById(id);
14102        if(existing){
14103            existing.parentNode.removeChild(existing);
14104        }
14105    },
14106
14107    /**
14108     * Dynamically swaps an existing stylesheet reference for a new one
14109     * @param {String} id The id of an existing link tag to remove
14110     * @param {String} url The href of the new stylesheet to include
14111     */
14112    swapStyleSheet : function(id, url){
14113        this.removeStyleSheet(id);
14114        var ss = doc.createElement("link");
14115        ss.setAttribute("rel", "stylesheet");
14116        ss.setAttribute("type", "text/css");
14117        ss.setAttribute("id", id);
14118        ss.setAttribute("href", url);
14119        doc.getElementsByTagName("head")[0].appendChild(ss);
14120    },
14121    
14122    /**
14123     * Refresh the rule cache if you have dynamically added stylesheets
14124     * @return {Object} An object (hash) of rules indexed by selector
14125     */
14126    refreshCache : function(){
14127        return this.getRules(true);
14128    },
14129
14130    // private
14131    cacheStyleSheet : function(stylesheet){
14132        if(!rules){
14133            rules = {};
14134        }
14135        try{// try catch for cross domain access issue
14136            var ssRules = stylesheet.cssRules || stylesheet.rules;
14137            for(var j = ssRules.length-1; j >= 0; --j){
14138                rules[ssRules[j].selectorText] = ssRules[j];
14139            }
14140        }catch(e){}
14141    },
14142    
14143    /**
14144     * Gets all css rules for the document
14145     * @param {Boolean} refreshCache true to refresh the internal cache
14146     * @return {Object} An object (hash) of rules indexed by selector
14147     */
14148    getRules : function(refreshCache){
14149                 if(rules == null || refreshCache){
14150                         rules = {};
14151                         var ds = doc.styleSheets;
14152                         for(var i =0, len = ds.length; i < len; i++){
14153                             try{
14154                         this.cacheStyleSheet(ds[i]);
14155                     }catch(e){} 
14156                 }
14157                 }
14158                 return rules;
14159         },
14160         
14161         /**
14162     * Gets an an individual CSS rule by selector(s)
14163     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14164     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14165     * @return {CSSRule} The CSS rule or null if one is not found
14166     */
14167    getRule : function(selector, refreshCache){
14168                 var rs = this.getRules(refreshCache);
14169                 if(!(selector instanceof Array)){
14170                     return rs[selector];
14171                 }
14172                 for(var i = 0; i < selector.length; i++){
14173                         if(rs[selector[i]]){
14174                                 return rs[selector[i]];
14175                         }
14176                 }
14177                 return null;
14178         },
14179         
14180         
14181         /**
14182     * Updates a rule property
14183     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14184     * @param {String} property The css property
14185     * @param {String} value The new value for the property
14186     * @return {Boolean} true If a rule was found and updated
14187     */
14188    updateRule : function(selector, property, value){
14189                 if(!(selector instanceof Array)){
14190                         var rule = this.getRule(selector);
14191                         if(rule){
14192                                 rule.style[property.replace(camelRe, camelFn)] = value;
14193                                 return true;
14194                         }
14195                 }else{
14196                         for(var i = 0; i < selector.length; i++){
14197                                 if(this.updateRule(selector[i], property, value)){
14198                                         return true;
14199                                 }
14200                         }
14201                 }
14202                 return false;
14203         }
14204    };   
14205 }();/*
14206  * Based on:
14207  * Ext JS Library 1.1.1
14208  * Copyright(c) 2006-2007, Ext JS, LLC.
14209  *
14210  * Originally Released Under LGPL - original licence link has changed is not relivant.
14211  *
14212  * Fork - LGPL
14213  * <script type="text/javascript">
14214  */
14215
14216  
14217
14218 /**
14219  * @class Roo.util.ClickRepeater
14220  * @extends Roo.util.Observable
14221  * 
14222  * A wrapper class which can be applied to any element. Fires a "click" event while the
14223  * mouse is pressed. The interval between firings may be specified in the config but
14224  * defaults to 10 milliseconds.
14225  * 
14226  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14227  * 
14228  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14229  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14230  * Similar to an autorepeat key delay.
14231  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14232  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14233  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14234  *           "interval" and "delay" are ignored. "immediate" is honored.
14235  * @cfg {Boolean} preventDefault True to prevent the default click event
14236  * @cfg {Boolean} stopDefault True to stop the default click event
14237  * 
14238  * @history
14239  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14240  *     2007-02-02 jvs Renamed to ClickRepeater
14241  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14242  *
14243  *  @constructor
14244  * @param {String/HTMLElement/Element} el The element to listen on
14245  * @param {Object} config
14246  **/
14247 Roo.util.ClickRepeater = function(el, config)
14248 {
14249     this.el = Roo.get(el);
14250     this.el.unselectable();
14251
14252     Roo.apply(this, config);
14253
14254     this.addEvents({
14255     /**
14256      * @event mousedown
14257      * Fires when the mouse button is depressed.
14258      * @param {Roo.util.ClickRepeater} this
14259      */
14260         "mousedown" : true,
14261     /**
14262      * @event click
14263      * Fires on a specified interval during the time the element is pressed.
14264      * @param {Roo.util.ClickRepeater} this
14265      */
14266         "click" : true,
14267     /**
14268      * @event mouseup
14269      * Fires when the mouse key is released.
14270      * @param {Roo.util.ClickRepeater} this
14271      */
14272         "mouseup" : true
14273     });
14274
14275     this.el.on("mousedown", this.handleMouseDown, this);
14276     if(this.preventDefault || this.stopDefault){
14277         this.el.on("click", function(e){
14278             if(this.preventDefault){
14279                 e.preventDefault();
14280             }
14281             if(this.stopDefault){
14282                 e.stopEvent();
14283             }
14284         }, this);
14285     }
14286
14287     // allow inline handler
14288     if(this.handler){
14289         this.on("click", this.handler,  this.scope || this);
14290     }
14291
14292     Roo.util.ClickRepeater.superclass.constructor.call(this);
14293 };
14294
14295 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14296     interval : 20,
14297     delay: 250,
14298     preventDefault : true,
14299     stopDefault : false,
14300     timer : 0,
14301
14302     // private
14303     handleMouseDown : function(){
14304         clearTimeout(this.timer);
14305         this.el.blur();
14306         if(this.pressClass){
14307             this.el.addClass(this.pressClass);
14308         }
14309         this.mousedownTime = new Date();
14310
14311         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14312         this.el.on("mouseout", this.handleMouseOut, this);
14313
14314         this.fireEvent("mousedown", this);
14315         this.fireEvent("click", this);
14316         
14317         this.timer = this.click.defer(this.delay || this.interval, this);
14318     },
14319
14320     // private
14321     click : function(){
14322         this.fireEvent("click", this);
14323         this.timer = this.click.defer(this.getInterval(), this);
14324     },
14325
14326     // private
14327     getInterval: function(){
14328         if(!this.accelerate){
14329             return this.interval;
14330         }
14331         var pressTime = this.mousedownTime.getElapsed();
14332         if(pressTime < 500){
14333             return 400;
14334         }else if(pressTime < 1700){
14335             return 320;
14336         }else if(pressTime < 2600){
14337             return 250;
14338         }else if(pressTime < 3500){
14339             return 180;
14340         }else if(pressTime < 4400){
14341             return 140;
14342         }else if(pressTime < 5300){
14343             return 80;
14344         }else if(pressTime < 6200){
14345             return 50;
14346         }else{
14347             return 10;
14348         }
14349     },
14350
14351     // private
14352     handleMouseOut : function(){
14353         clearTimeout(this.timer);
14354         if(this.pressClass){
14355             this.el.removeClass(this.pressClass);
14356         }
14357         this.el.on("mouseover", this.handleMouseReturn, this);
14358     },
14359
14360     // private
14361     handleMouseReturn : function(){
14362         this.el.un("mouseover", this.handleMouseReturn);
14363         if(this.pressClass){
14364             this.el.addClass(this.pressClass);
14365         }
14366         this.click();
14367     },
14368
14369     // private
14370     handleMouseUp : function(){
14371         clearTimeout(this.timer);
14372         this.el.un("mouseover", this.handleMouseReturn);
14373         this.el.un("mouseout", this.handleMouseOut);
14374         Roo.get(document).un("mouseup", this.handleMouseUp);
14375         this.el.removeClass(this.pressClass);
14376         this.fireEvent("mouseup", this);
14377     }
14378 });/*
14379  * Based on:
14380  * Ext JS Library 1.1.1
14381  * Copyright(c) 2006-2007, Ext JS, LLC.
14382  *
14383  * Originally Released Under LGPL - original licence link has changed is not relivant.
14384  *
14385  * Fork - LGPL
14386  * <script type="text/javascript">
14387  */
14388
14389  
14390 /**
14391  * @class Roo.KeyNav
14392  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14393  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14394  * way to implement custom navigation schemes for any UI component.</p>
14395  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14396  * pageUp, pageDown, del, home, end.  Usage:</p>
14397  <pre><code>
14398 var nav = new Roo.KeyNav("my-element", {
14399     "left" : function(e){
14400         this.moveLeft(e.ctrlKey);
14401     },
14402     "right" : function(e){
14403         this.moveRight(e.ctrlKey);
14404     },
14405     "enter" : function(e){
14406         this.save();
14407     },
14408     scope : this
14409 });
14410 </code></pre>
14411  * @constructor
14412  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14413  * @param {Object} config The config
14414  */
14415 Roo.KeyNav = function(el, config){
14416     this.el = Roo.get(el);
14417     Roo.apply(this, config);
14418     if(!this.disabled){
14419         this.disabled = true;
14420         this.enable();
14421     }
14422 };
14423
14424 Roo.KeyNav.prototype = {
14425     /**
14426      * @cfg {Boolean} disabled
14427      * True to disable this KeyNav instance (defaults to false)
14428      */
14429     disabled : false,
14430     /**
14431      * @cfg {String} defaultEventAction
14432      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14433      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14434      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14435      */
14436     defaultEventAction: "stopEvent",
14437     /**
14438      * @cfg {Boolean} forceKeyDown
14439      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14440      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14441      * handle keydown instead of keypress.
14442      */
14443     forceKeyDown : false,
14444
14445     // private
14446     prepareEvent : function(e){
14447         var k = e.getKey();
14448         var h = this.keyToHandler[k];
14449         //if(h && this[h]){
14450         //    e.stopPropagation();
14451         //}
14452         if(Roo.isSafari && h && k >= 37 && k <= 40){
14453             e.stopEvent();
14454         }
14455     },
14456
14457     // private
14458     relay : function(e){
14459         var k = e.getKey();
14460         var h = this.keyToHandler[k];
14461         if(h && this[h]){
14462             if(this.doRelay(e, this[h], h) !== true){
14463                 e[this.defaultEventAction]();
14464             }
14465         }
14466     },
14467
14468     // private
14469     doRelay : function(e, h, hname){
14470         return h.call(this.scope || this, e);
14471     },
14472
14473     // possible handlers
14474     enter : false,
14475     left : false,
14476     right : false,
14477     up : false,
14478     down : false,
14479     tab : false,
14480     esc : false,
14481     pageUp : false,
14482     pageDown : false,
14483     del : false,
14484     home : false,
14485     end : false,
14486
14487     // quick lookup hash
14488     keyToHandler : {
14489         37 : "left",
14490         39 : "right",
14491         38 : "up",
14492         40 : "down",
14493         33 : "pageUp",
14494         34 : "pageDown",
14495         46 : "del",
14496         36 : "home",
14497         35 : "end",
14498         13 : "enter",
14499         27 : "esc",
14500         9  : "tab"
14501     },
14502
14503         /**
14504          * Enable this KeyNav
14505          */
14506         enable: function(){
14507                 if(this.disabled){
14508             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14509             // the EventObject will normalize Safari automatically
14510             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14511                 this.el.on("keydown", this.relay,  this);
14512             }else{
14513                 this.el.on("keydown", this.prepareEvent,  this);
14514                 this.el.on("keypress", this.relay,  this);
14515             }
14516                     this.disabled = false;
14517                 }
14518         },
14519
14520         /**
14521          * Disable this KeyNav
14522          */
14523         disable: function(){
14524                 if(!this.disabled){
14525                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14526                 this.el.un("keydown", this.relay);
14527             }else{
14528                 this.el.un("keydown", this.prepareEvent);
14529                 this.el.un("keypress", this.relay);
14530             }
14531                     this.disabled = true;
14532                 }
14533         }
14534 };/*
14535  * Based on:
14536  * Ext JS Library 1.1.1
14537  * Copyright(c) 2006-2007, Ext JS, LLC.
14538  *
14539  * Originally Released Under LGPL - original licence link has changed is not relivant.
14540  *
14541  * Fork - LGPL
14542  * <script type="text/javascript">
14543  */
14544
14545  
14546 /**
14547  * @class Roo.KeyMap
14548  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14549  * The constructor accepts the same config object as defined by {@link #addBinding}.
14550  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14551  * combination it will call the function with this signature (if the match is a multi-key
14552  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14553  * A KeyMap can also handle a string representation of keys.<br />
14554  * Usage:
14555  <pre><code>
14556 // map one key by key code
14557 var map = new Roo.KeyMap("my-element", {
14558     key: 13, // or Roo.EventObject.ENTER
14559     fn: myHandler,
14560     scope: myObject
14561 });
14562
14563 // map multiple keys to one action by string
14564 var map = new Roo.KeyMap("my-element", {
14565     key: "a\r\n\t",
14566     fn: myHandler,
14567     scope: myObject
14568 });
14569
14570 // map multiple keys to multiple actions by strings and array of codes
14571 var map = new Roo.KeyMap("my-element", [
14572     {
14573         key: [10,13],
14574         fn: function(){ alert("Return was pressed"); }
14575     }, {
14576         key: "abc",
14577         fn: function(){ alert('a, b or c was pressed'); }
14578     }, {
14579         key: "\t",
14580         ctrl:true,
14581         shift:true,
14582         fn: function(){ alert('Control + shift + tab was pressed.'); }
14583     }
14584 ]);
14585 </code></pre>
14586  * <b>Note: A KeyMap starts enabled</b>
14587  * @constructor
14588  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14589  * @param {Object} config The config (see {@link #addBinding})
14590  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14591  */
14592 Roo.KeyMap = function(el, config, eventName){
14593     this.el  = Roo.get(el);
14594     this.eventName = eventName || "keydown";
14595     this.bindings = [];
14596     if(config){
14597         this.addBinding(config);
14598     }
14599     this.enable();
14600 };
14601
14602 Roo.KeyMap.prototype = {
14603     /**
14604      * True to stop the event from bubbling and prevent the default browser action if the
14605      * key was handled by the KeyMap (defaults to false)
14606      * @type Boolean
14607      */
14608     stopEvent : false,
14609
14610     /**
14611      * Add a new binding to this KeyMap. The following config object properties are supported:
14612      * <pre>
14613 Property    Type             Description
14614 ----------  ---------------  ----------------------------------------------------------------------
14615 key         String/Array     A single keycode or an array of keycodes to handle
14616 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14617 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14618 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14619 fn          Function         The function to call when KeyMap finds the expected key combination
14620 scope       Object           The scope of the callback function
14621 </pre>
14622      *
14623      * Usage:
14624      * <pre><code>
14625 // Create a KeyMap
14626 var map = new Roo.KeyMap(document, {
14627     key: Roo.EventObject.ENTER,
14628     fn: handleKey,
14629     scope: this
14630 });
14631
14632 //Add a new binding to the existing KeyMap later
14633 map.addBinding({
14634     key: 'abc',
14635     shift: true,
14636     fn: handleKey,
14637     scope: this
14638 });
14639 </code></pre>
14640      * @param {Object/Array} config A single KeyMap config or an array of configs
14641      */
14642         addBinding : function(config){
14643         if(config instanceof Array){
14644             for(var i = 0, len = config.length; i < len; i++){
14645                 this.addBinding(config[i]);
14646             }
14647             return;
14648         }
14649         var keyCode = config.key,
14650             shift = config.shift, 
14651             ctrl = config.ctrl, 
14652             alt = config.alt,
14653             fn = config.fn,
14654             scope = config.scope;
14655         if(typeof keyCode == "string"){
14656             var ks = [];
14657             var keyString = keyCode.toUpperCase();
14658             for(var j = 0, len = keyString.length; j < len; j++){
14659                 ks.push(keyString.charCodeAt(j));
14660             }
14661             keyCode = ks;
14662         }
14663         var keyArray = keyCode instanceof Array;
14664         var handler = function(e){
14665             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14666                 var k = e.getKey();
14667                 if(keyArray){
14668                     for(var i = 0, len = keyCode.length; i < len; i++){
14669                         if(keyCode[i] == k){
14670                           if(this.stopEvent){
14671                               e.stopEvent();
14672                           }
14673                           fn.call(scope || window, k, e);
14674                           return;
14675                         }
14676                     }
14677                 }else{
14678                     if(k == keyCode){
14679                         if(this.stopEvent){
14680                            e.stopEvent();
14681                         }
14682                         fn.call(scope || window, k, e);
14683                     }
14684                 }
14685             }
14686         };
14687         this.bindings.push(handler);  
14688         },
14689
14690     /**
14691      * Shorthand for adding a single key listener
14692      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14693      * following options:
14694      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14695      * @param {Function} fn The function to call
14696      * @param {Object} scope (optional) The scope of the function
14697      */
14698     on : function(key, fn, scope){
14699         var keyCode, shift, ctrl, alt;
14700         if(typeof key == "object" && !(key instanceof Array)){
14701             keyCode = key.key;
14702             shift = key.shift;
14703             ctrl = key.ctrl;
14704             alt = key.alt;
14705         }else{
14706             keyCode = key;
14707         }
14708         this.addBinding({
14709             key: keyCode,
14710             shift: shift,
14711             ctrl: ctrl,
14712             alt: alt,
14713             fn: fn,
14714             scope: scope
14715         })
14716     },
14717
14718     // private
14719     handleKeyDown : function(e){
14720             if(this.enabled){ //just in case
14721             var b = this.bindings;
14722             for(var i = 0, len = b.length; i < len; i++){
14723                 b[i].call(this, e);
14724             }
14725             }
14726         },
14727         
14728         /**
14729          * Returns true if this KeyMap is enabled
14730          * @return {Boolean} 
14731          */
14732         isEnabled : function(){
14733             return this.enabled;  
14734         },
14735         
14736         /**
14737          * Enables this KeyMap
14738          */
14739         enable: function(){
14740                 if(!this.enabled){
14741                     this.el.on(this.eventName, this.handleKeyDown, this);
14742                     this.enabled = true;
14743                 }
14744         },
14745
14746         /**
14747          * Disable this KeyMap
14748          */
14749         disable: function(){
14750                 if(this.enabled){
14751                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14752                     this.enabled = false;
14753                 }
14754         }
14755 };/*
14756  * Based on:
14757  * Ext JS Library 1.1.1
14758  * Copyright(c) 2006-2007, Ext JS, LLC.
14759  *
14760  * Originally Released Under LGPL - original licence link has changed is not relivant.
14761  *
14762  * Fork - LGPL
14763  * <script type="text/javascript">
14764  */
14765
14766  
14767 /**
14768  * @class Roo.util.TextMetrics
14769  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14770  * wide, in pixels, a given block of text will be.
14771  * @singleton
14772  */
14773 Roo.util.TextMetrics = function(){
14774     var shared;
14775     return {
14776         /**
14777          * Measures the size of the specified text
14778          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14779          * that can affect the size of the rendered text
14780          * @param {String} text The text to measure
14781          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14782          * in order to accurately measure the text height
14783          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14784          */
14785         measure : function(el, text, fixedWidth){
14786             if(!shared){
14787                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14788             }
14789             shared.bind(el);
14790             shared.setFixedWidth(fixedWidth || 'auto');
14791             return shared.getSize(text);
14792         },
14793
14794         /**
14795          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14796          * the overhead of multiple calls to initialize the style properties on each measurement.
14797          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14798          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14799          * in order to accurately measure the text height
14800          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14801          */
14802         createInstance : function(el, fixedWidth){
14803             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14804         }
14805     };
14806 }();
14807
14808  
14809
14810 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14811     var ml = new Roo.Element(document.createElement('div'));
14812     document.body.appendChild(ml.dom);
14813     ml.position('absolute');
14814     ml.setLeftTop(-1000, -1000);
14815     ml.hide();
14816
14817     if(fixedWidth){
14818         ml.setWidth(fixedWidth);
14819     }
14820      
14821     var instance = {
14822         /**
14823          * Returns the size of the specified text based on the internal element's style and width properties
14824          * @memberOf Roo.util.TextMetrics.Instance#
14825          * @param {String} text The text to measure
14826          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14827          */
14828         getSize : function(text){
14829             ml.update(text);
14830             var s = ml.getSize();
14831             ml.update('');
14832             return s;
14833         },
14834
14835         /**
14836          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14837          * that can affect the size of the rendered text
14838          * @memberOf Roo.util.TextMetrics.Instance#
14839          * @param {String/HTMLElement} el The element, dom node or id
14840          */
14841         bind : function(el){
14842             ml.setStyle(
14843                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14844             );
14845         },
14846
14847         /**
14848          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14849          * to set a fixed width in order to accurately measure the text height.
14850          * @memberOf Roo.util.TextMetrics.Instance#
14851          * @param {Number} width The width to set on the element
14852          */
14853         setFixedWidth : function(width){
14854             ml.setWidth(width);
14855         },
14856
14857         /**
14858          * Returns the measured width of the specified text
14859          * @memberOf Roo.util.TextMetrics.Instance#
14860          * @param {String} text The text to measure
14861          * @return {Number} width The width in pixels
14862          */
14863         getWidth : function(text){
14864             ml.dom.style.width = 'auto';
14865             return this.getSize(text).width;
14866         },
14867
14868         /**
14869          * Returns the measured height of the specified text.  For multiline text, be sure to call
14870          * {@link #setFixedWidth} if necessary.
14871          * @memberOf Roo.util.TextMetrics.Instance#
14872          * @param {String} text The text to measure
14873          * @return {Number} height The height in pixels
14874          */
14875         getHeight : function(text){
14876             return this.getSize(text).height;
14877         }
14878     };
14879
14880     instance.bind(bindTo);
14881
14882     return instance;
14883 };
14884
14885 // backwards compat
14886 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14887  * Based on:
14888  * Ext JS Library 1.1.1
14889  * Copyright(c) 2006-2007, Ext JS, LLC.
14890  *
14891  * Originally Released Under LGPL - original licence link has changed is not relivant.
14892  *
14893  * Fork - LGPL
14894  * <script type="text/javascript">
14895  */
14896
14897 /**
14898  * @class Roo.state.Provider
14899  * Abstract base class for state provider implementations. This class provides methods
14900  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14901  * Provider interface.
14902  */
14903 Roo.state.Provider = function(){
14904     /**
14905      * @event statechange
14906      * Fires when a state change occurs.
14907      * @param {Provider} this This state provider
14908      * @param {String} key The state key which was changed
14909      * @param {String} value The encoded value for the state
14910      */
14911     this.addEvents({
14912         "statechange": true
14913     });
14914     this.state = {};
14915     Roo.state.Provider.superclass.constructor.call(this);
14916 };
14917 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14918     /**
14919      * Returns the current value for a key
14920      * @param {String} name The key name
14921      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14922      * @return {Mixed} The state data
14923      */
14924     get : function(name, defaultValue){
14925         return typeof this.state[name] == "undefined" ?
14926             defaultValue : this.state[name];
14927     },
14928     
14929     /**
14930      * Clears a value from the state
14931      * @param {String} name The key name
14932      */
14933     clear : function(name){
14934         delete this.state[name];
14935         this.fireEvent("statechange", this, name, null);
14936     },
14937     
14938     /**
14939      * Sets the value for a key
14940      * @param {String} name The key name
14941      * @param {Mixed} value The value to set
14942      */
14943     set : function(name, value){
14944         this.state[name] = value;
14945         this.fireEvent("statechange", this, name, value);
14946     },
14947     
14948     /**
14949      * Decodes a string previously encoded with {@link #encodeValue}.
14950      * @param {String} value The value to decode
14951      * @return {Mixed} The decoded value
14952      */
14953     decodeValue : function(cookie){
14954         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14955         var matches = re.exec(unescape(cookie));
14956         if(!matches || !matches[1]) {
14957             return; // non state cookie
14958         }
14959         var type = matches[1];
14960         var v = matches[2];
14961         switch(type){
14962             case "n":
14963                 return parseFloat(v);
14964             case "d":
14965                 return new Date(Date.parse(v));
14966             case "b":
14967                 return (v == "1");
14968             case "a":
14969                 var all = [];
14970                 var values = v.split("^");
14971                 for(var i = 0, len = values.length; i < len; i++){
14972                     all.push(this.decodeValue(values[i]));
14973                 }
14974                 return all;
14975            case "o":
14976                 var all = {};
14977                 var values = v.split("^");
14978                 for(var i = 0, len = values.length; i < len; i++){
14979                     var kv = values[i].split("=");
14980                     all[kv[0]] = this.decodeValue(kv[1]);
14981                 }
14982                 return all;
14983            default:
14984                 return v;
14985         }
14986     },
14987     
14988     /**
14989      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14990      * @param {Mixed} value The value to encode
14991      * @return {String} The encoded value
14992      */
14993     encodeValue : function(v){
14994         var enc;
14995         if(typeof v == "number"){
14996             enc = "n:" + v;
14997         }else if(typeof v == "boolean"){
14998             enc = "b:" + (v ? "1" : "0");
14999         }else if(v instanceof Date){
15000             enc = "d:" + v.toGMTString();
15001         }else if(v instanceof Array){
15002             var flat = "";
15003             for(var i = 0, len = v.length; i < len; i++){
15004                 flat += this.encodeValue(v[i]);
15005                 if(i != len-1) {
15006                     flat += "^";
15007                 }
15008             }
15009             enc = "a:" + flat;
15010         }else if(typeof v == "object"){
15011             var flat = "";
15012             for(var key in v){
15013                 if(typeof v[key] != "function"){
15014                     flat += key + "=" + this.encodeValue(v[key]) + "^";
15015                 }
15016             }
15017             enc = "o:" + flat.substring(0, flat.length-1);
15018         }else{
15019             enc = "s:" + v;
15020         }
15021         return escape(enc);        
15022     }
15023 });
15024
15025 /*
15026  * Based on:
15027  * Ext JS Library 1.1.1
15028  * Copyright(c) 2006-2007, Ext JS, LLC.
15029  *
15030  * Originally Released Under LGPL - original licence link has changed is not relivant.
15031  *
15032  * Fork - LGPL
15033  * <script type="text/javascript">
15034  */
15035 /**
15036  * @class Roo.state.Manager
15037  * This is the global state manager. By default all components that are "state aware" check this class
15038  * for state information if you don't pass them a custom state provider. In order for this class
15039  * to be useful, it must be initialized with a provider when your application initializes.
15040  <pre><code>
15041 // in your initialization function
15042 init : function(){
15043    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
15044    ...
15045    // supposed you have a {@link Roo.BorderLayout}
15046    var layout = new Roo.BorderLayout(...);
15047    layout.restoreState();
15048    // or a {Roo.BasicDialog}
15049    var dialog = new Roo.BasicDialog(...);
15050    dialog.restoreState();
15051  </code></pre>
15052  * @singleton
15053  */
15054 Roo.state.Manager = function(){
15055     var provider = new Roo.state.Provider();
15056     
15057     return {
15058         /**
15059          * Configures the default state provider for your application
15060          * @param {Provider} stateProvider The state provider to set
15061          */
15062         setProvider : function(stateProvider){
15063             provider = stateProvider;
15064         },
15065         
15066         /**
15067          * Returns the current value for a key
15068          * @param {String} name The key name
15069          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15070          * @return {Mixed} The state data
15071          */
15072         get : function(key, defaultValue){
15073             return provider.get(key, defaultValue);
15074         },
15075         
15076         /**
15077          * Sets the value for a key
15078          * @param {String} name The key name
15079          * @param {Mixed} value The state data
15080          */
15081          set : function(key, value){
15082             provider.set(key, value);
15083         },
15084         
15085         /**
15086          * Clears a value from the state
15087          * @param {String} name The key name
15088          */
15089         clear : function(key){
15090             provider.clear(key);
15091         },
15092         
15093         /**
15094          * Gets the currently configured state provider
15095          * @return {Provider} The state provider
15096          */
15097         getProvider : function(){
15098             return provider;
15099         }
15100     };
15101 }();
15102 /*
15103  * Based on:
15104  * Ext JS Library 1.1.1
15105  * Copyright(c) 2006-2007, Ext JS, LLC.
15106  *
15107  * Originally Released Under LGPL - original licence link has changed is not relivant.
15108  *
15109  * Fork - LGPL
15110  * <script type="text/javascript">
15111  */
15112 /**
15113  * @class Roo.state.CookieProvider
15114  * @extends Roo.state.Provider
15115  * The default Provider implementation which saves state via cookies.
15116  * <br />Usage:
15117  <pre><code>
15118    var cp = new Roo.state.CookieProvider({
15119        path: "/cgi-bin/",
15120        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15121        domain: "roojs.com"
15122    })
15123    Roo.state.Manager.setProvider(cp);
15124  </code></pre>
15125  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15126  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15127  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15128  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15129  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15130  * domain the page is running on including the 'www' like 'www.roojs.com')
15131  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15132  * @constructor
15133  * Create a new CookieProvider
15134  * @param {Object} config The configuration object
15135  */
15136 Roo.state.CookieProvider = function(config){
15137     Roo.state.CookieProvider.superclass.constructor.call(this);
15138     this.path = "/";
15139     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15140     this.domain = null;
15141     this.secure = false;
15142     Roo.apply(this, config);
15143     this.state = this.readCookies();
15144 };
15145
15146 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15147     // private
15148     set : function(name, value){
15149         if(typeof value == "undefined" || value === null){
15150             this.clear(name);
15151             return;
15152         }
15153         this.setCookie(name, value);
15154         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15155     },
15156
15157     // private
15158     clear : function(name){
15159         this.clearCookie(name);
15160         Roo.state.CookieProvider.superclass.clear.call(this, name);
15161     },
15162
15163     // private
15164     readCookies : function(){
15165         var cookies = {};
15166         var c = document.cookie + ";";
15167         var re = /\s?(.*?)=(.*?);/g;
15168         var matches;
15169         while((matches = re.exec(c)) != null){
15170             var name = matches[1];
15171             var value = matches[2];
15172             if(name && name.substring(0,3) == "ys-"){
15173                 cookies[name.substr(3)] = this.decodeValue(value);
15174             }
15175         }
15176         return cookies;
15177     },
15178
15179     // private
15180     setCookie : function(name, value){
15181         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15182            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15183            ((this.path == null) ? "" : ("; path=" + this.path)) +
15184            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15185            ((this.secure == true) ? "; secure" : "");
15186     },
15187
15188     // private
15189     clearCookie : function(name){
15190         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15191            ((this.path == null) ? "" : ("; path=" + this.path)) +
15192            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15193            ((this.secure == true) ? "; secure" : "");
15194     }
15195 });/*
15196  * Based on:
15197  * Ext JS Library 1.1.1
15198  * Copyright(c) 2006-2007, Ext JS, LLC.
15199  *
15200  * Originally Released Under LGPL - original licence link has changed is not relivant.
15201  *
15202  * Fork - LGPL
15203  * <script type="text/javascript">
15204  */
15205  
15206
15207 /**
15208  * @class Roo.ComponentMgr
15209  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15210  * @singleton
15211  */
15212 Roo.ComponentMgr = function(){
15213     var all = new Roo.util.MixedCollection();
15214
15215     return {
15216         /**
15217          * Registers a component.
15218          * @param {Roo.Component} c The component
15219          */
15220         register : function(c){
15221             all.add(c);
15222         },
15223
15224         /**
15225          * Unregisters a component.
15226          * @param {Roo.Component} c The component
15227          */
15228         unregister : function(c){
15229             all.remove(c);
15230         },
15231
15232         /**
15233          * Returns a component by id
15234          * @param {String} id The component id
15235          */
15236         get : function(id){
15237             return all.get(id);
15238         },
15239
15240         /**
15241          * Registers a function that will be called when a specified component is added to ComponentMgr
15242          * @param {String} id The component id
15243          * @param {Funtction} fn The callback function
15244          * @param {Object} scope The scope of the callback
15245          */
15246         onAvailable : function(id, fn, scope){
15247             all.on("add", function(index, o){
15248                 if(o.id == id){
15249                     fn.call(scope || o, o);
15250                     all.un("add", fn, scope);
15251                 }
15252             });
15253         }
15254     };
15255 }();/*
15256  * Based on:
15257  * Ext JS Library 1.1.1
15258  * Copyright(c) 2006-2007, Ext JS, LLC.
15259  *
15260  * Originally Released Under LGPL - original licence link has changed is not relivant.
15261  *
15262  * Fork - LGPL
15263  * <script type="text/javascript">
15264  */
15265  
15266 /**
15267  * @class Roo.Component
15268  * @extends Roo.util.Observable
15269  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15270  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15271  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15272  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15273  * All visual components (widgets) that require rendering into a layout should subclass Component.
15274  * @constructor
15275  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15276  * 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
15277  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15278  */
15279 Roo.Component = function(config){
15280     config = config || {};
15281     if(config.tagName || config.dom || typeof config == "string"){ // element object
15282         config = {el: config, id: config.id || config};
15283     }
15284     this.initialConfig = config;
15285
15286     Roo.apply(this, config);
15287     this.addEvents({
15288         /**
15289          * @event disable
15290          * Fires after the component is disabled.
15291              * @param {Roo.Component} this
15292              */
15293         disable : true,
15294         /**
15295          * @event enable
15296          * Fires after the component is enabled.
15297              * @param {Roo.Component} this
15298              */
15299         enable : true,
15300         /**
15301          * @event beforeshow
15302          * Fires before the component is shown.  Return false to stop the show.
15303              * @param {Roo.Component} this
15304              */
15305         beforeshow : true,
15306         /**
15307          * @event show
15308          * Fires after the component is shown.
15309              * @param {Roo.Component} this
15310              */
15311         show : true,
15312         /**
15313          * @event beforehide
15314          * Fires before the component is hidden. Return false to stop the hide.
15315              * @param {Roo.Component} this
15316              */
15317         beforehide : true,
15318         /**
15319          * @event hide
15320          * Fires after the component is hidden.
15321              * @param {Roo.Component} this
15322              */
15323         hide : true,
15324         /**
15325          * @event beforerender
15326          * Fires before the component is rendered. Return false to stop the render.
15327              * @param {Roo.Component} this
15328              */
15329         beforerender : true,
15330         /**
15331          * @event render
15332          * Fires after the component is rendered.
15333              * @param {Roo.Component} this
15334              */
15335         render : true,
15336         /**
15337          * @event beforedestroy
15338          * Fires before the component is destroyed. Return false to stop the destroy.
15339              * @param {Roo.Component} this
15340              */
15341         beforedestroy : true,
15342         /**
15343          * @event destroy
15344          * Fires after the component is destroyed.
15345              * @param {Roo.Component} this
15346              */
15347         destroy : true
15348     });
15349     if(!this.id){
15350         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15351     }
15352     Roo.ComponentMgr.register(this);
15353     Roo.Component.superclass.constructor.call(this);
15354     this.initComponent();
15355     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15356         this.render(this.renderTo);
15357         delete this.renderTo;
15358     }
15359 };
15360
15361 /** @private */
15362 Roo.Component.AUTO_ID = 1000;
15363
15364 Roo.extend(Roo.Component, Roo.util.Observable, {
15365     /**
15366      * @scope Roo.Component.prototype
15367      * @type {Boolean}
15368      * true if this component is hidden. Read-only.
15369      */
15370     hidden : false,
15371     /**
15372      * @type {Boolean}
15373      * true if this component is disabled. Read-only.
15374      */
15375     disabled : false,
15376     /**
15377      * @type {Boolean}
15378      * true if this component has been rendered. Read-only.
15379      */
15380     rendered : false,
15381     
15382     /** @cfg {String} disableClass
15383      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15384      */
15385     disabledClass : "x-item-disabled",
15386         /** @cfg {Boolean} allowDomMove
15387          * Whether the component can move the Dom node when rendering (defaults to true).
15388          */
15389     allowDomMove : true,
15390     /** @cfg {String} hideMode (display|visibility)
15391      * How this component should hidden. Supported values are
15392      * "visibility" (css visibility), "offsets" (negative offset position) and
15393      * "display" (css display) - defaults to "display".
15394      */
15395     hideMode: 'display',
15396
15397     /** @private */
15398     ctype : "Roo.Component",
15399
15400     /**
15401      * @cfg {String} actionMode 
15402      * which property holds the element that used for  hide() / show() / disable() / enable()
15403      * default is 'el' 
15404      */
15405     actionMode : "el",
15406
15407     /** @private */
15408     getActionEl : function(){
15409         return this[this.actionMode];
15410     },
15411
15412     initComponent : Roo.emptyFn,
15413     /**
15414      * If this is a lazy rendering component, render it to its container element.
15415      * @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.
15416      */
15417     render : function(container, position){
15418         
15419         if(this.rendered){
15420             return this;
15421         }
15422         
15423         if(this.fireEvent("beforerender", this) === false){
15424             return false;
15425         }
15426         
15427         if(!container && this.el){
15428             this.el = Roo.get(this.el);
15429             container = this.el.dom.parentNode;
15430             this.allowDomMove = false;
15431         }
15432         this.container = Roo.get(container);
15433         this.rendered = true;
15434         if(position !== undefined){
15435             if(typeof position == 'number'){
15436                 position = this.container.dom.childNodes[position];
15437             }else{
15438                 position = Roo.getDom(position);
15439             }
15440         }
15441         this.onRender(this.container, position || null);
15442         if(this.cls){
15443             this.el.addClass(this.cls);
15444             delete this.cls;
15445         }
15446         if(this.style){
15447             this.el.applyStyles(this.style);
15448             delete this.style;
15449         }
15450         this.fireEvent("render", this);
15451         this.afterRender(this.container);
15452         if(this.hidden){
15453             this.hide();
15454         }
15455         if(this.disabled){
15456             this.disable();
15457         }
15458
15459         return this;
15460         
15461     },
15462
15463     /** @private */
15464     // default function is not really useful
15465     onRender : function(ct, position){
15466         if(this.el){
15467             this.el = Roo.get(this.el);
15468             if(this.allowDomMove !== false){
15469                 ct.dom.insertBefore(this.el.dom, position);
15470             }
15471         }
15472     },
15473
15474     /** @private */
15475     getAutoCreate : function(){
15476         var cfg = typeof this.autoCreate == "object" ?
15477                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15478         if(this.id && !cfg.id){
15479             cfg.id = this.id;
15480         }
15481         return cfg;
15482     },
15483
15484     /** @private */
15485     afterRender : Roo.emptyFn,
15486
15487     /**
15488      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15489      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15490      */
15491     destroy : function(){
15492         if(this.fireEvent("beforedestroy", this) !== false){
15493             this.purgeListeners();
15494             this.beforeDestroy();
15495             if(this.rendered){
15496                 this.el.removeAllListeners();
15497                 this.el.remove();
15498                 if(this.actionMode == "container"){
15499                     this.container.remove();
15500                 }
15501             }
15502             this.onDestroy();
15503             Roo.ComponentMgr.unregister(this);
15504             this.fireEvent("destroy", this);
15505         }
15506     },
15507
15508         /** @private */
15509     beforeDestroy : function(){
15510
15511     },
15512
15513         /** @private */
15514         onDestroy : function(){
15515
15516     },
15517
15518     /**
15519      * Returns the underlying {@link Roo.Element}.
15520      * @return {Roo.Element} The element
15521      */
15522     getEl : function(){
15523         return this.el;
15524     },
15525
15526     /**
15527      * Returns the id of this component.
15528      * @return {String}
15529      */
15530     getId : function(){
15531         return this.id;
15532     },
15533
15534     /**
15535      * Try to focus this component.
15536      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15537      * @return {Roo.Component} this
15538      */
15539     focus : function(selectText){
15540         if(this.rendered){
15541             this.el.focus();
15542             if(selectText === true){
15543                 this.el.dom.select();
15544             }
15545         }
15546         return this;
15547     },
15548
15549     /** @private */
15550     blur : function(){
15551         if(this.rendered){
15552             this.el.blur();
15553         }
15554         return this;
15555     },
15556
15557     /**
15558      * Disable this component.
15559      * @return {Roo.Component} this
15560      */
15561     disable : function(){
15562         if(this.rendered){
15563             this.onDisable();
15564         }
15565         this.disabled = true;
15566         this.fireEvent("disable", this);
15567         return this;
15568     },
15569
15570         // private
15571     onDisable : function(){
15572         this.getActionEl().addClass(this.disabledClass);
15573         this.el.dom.disabled = true;
15574     },
15575
15576     /**
15577      * Enable this component.
15578      * @return {Roo.Component} this
15579      */
15580     enable : function(){
15581         if(this.rendered){
15582             this.onEnable();
15583         }
15584         this.disabled = false;
15585         this.fireEvent("enable", this);
15586         return this;
15587     },
15588
15589         // private
15590     onEnable : function(){
15591         this.getActionEl().removeClass(this.disabledClass);
15592         this.el.dom.disabled = false;
15593     },
15594
15595     /**
15596      * Convenience function for setting disabled/enabled by boolean.
15597      * @param {Boolean} disabled
15598      */
15599     setDisabled : function(disabled){
15600         this[disabled ? "disable" : "enable"]();
15601     },
15602
15603     /**
15604      * Show this component.
15605      * @return {Roo.Component} this
15606      */
15607     show: function(){
15608         if(this.fireEvent("beforeshow", this) !== false){
15609             this.hidden = false;
15610             if(this.rendered){
15611                 this.onShow();
15612             }
15613             this.fireEvent("show", this);
15614         }
15615         return this;
15616     },
15617
15618     // private
15619     onShow : function(){
15620         var ae = this.getActionEl();
15621         if(this.hideMode == 'visibility'){
15622             ae.dom.style.visibility = "visible";
15623         }else if(this.hideMode == 'offsets'){
15624             ae.removeClass('x-hidden');
15625         }else{
15626             ae.dom.style.display = "";
15627         }
15628     },
15629
15630     /**
15631      * Hide this component.
15632      * @return {Roo.Component} this
15633      */
15634     hide: function(){
15635         if(this.fireEvent("beforehide", this) !== false){
15636             this.hidden = true;
15637             if(this.rendered){
15638                 this.onHide();
15639             }
15640             this.fireEvent("hide", this);
15641         }
15642         return this;
15643     },
15644
15645     // private
15646     onHide : function(){
15647         var ae = this.getActionEl();
15648         if(this.hideMode == 'visibility'){
15649             ae.dom.style.visibility = "hidden";
15650         }else if(this.hideMode == 'offsets'){
15651             ae.addClass('x-hidden');
15652         }else{
15653             ae.dom.style.display = "none";
15654         }
15655     },
15656
15657     /**
15658      * Convenience function to hide or show this component by boolean.
15659      * @param {Boolean} visible True to show, false to hide
15660      * @return {Roo.Component} this
15661      */
15662     setVisible: function(visible){
15663         if(visible) {
15664             this.show();
15665         }else{
15666             this.hide();
15667         }
15668         return this;
15669     },
15670
15671     /**
15672      * Returns true if this component is visible.
15673      */
15674     isVisible : function(){
15675         return this.getActionEl().isVisible();
15676     },
15677
15678     cloneConfig : function(overrides){
15679         overrides = overrides || {};
15680         var id = overrides.id || Roo.id();
15681         var cfg = Roo.applyIf(overrides, this.initialConfig);
15682         cfg.id = id; // prevent dup id
15683         return new this.constructor(cfg);
15684     }
15685 });/*
15686  * Based on:
15687  * Ext JS Library 1.1.1
15688  * Copyright(c) 2006-2007, Ext JS, LLC.
15689  *
15690  * Originally Released Under LGPL - original licence link has changed is not relivant.
15691  *
15692  * Fork - LGPL
15693  * <script type="text/javascript">
15694  */
15695
15696 /**
15697  * @class Roo.BoxComponent
15698  * @extends Roo.Component
15699  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15700  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15701  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15702  * layout containers.
15703  * @constructor
15704  * @param {Roo.Element/String/Object} config The configuration options.
15705  */
15706 Roo.BoxComponent = function(config){
15707     Roo.Component.call(this, config);
15708     this.addEvents({
15709         /**
15710          * @event resize
15711          * Fires after the component is resized.
15712              * @param {Roo.Component} this
15713              * @param {Number} adjWidth The box-adjusted width that was set
15714              * @param {Number} adjHeight The box-adjusted height that was set
15715              * @param {Number} rawWidth The width that was originally specified
15716              * @param {Number} rawHeight The height that was originally specified
15717              */
15718         resize : true,
15719         /**
15720          * @event move
15721          * Fires after the component is moved.
15722              * @param {Roo.Component} this
15723              * @param {Number} x The new x position
15724              * @param {Number} y The new y position
15725              */
15726         move : true
15727     });
15728 };
15729
15730 Roo.extend(Roo.BoxComponent, Roo.Component, {
15731     // private, set in afterRender to signify that the component has been rendered
15732     boxReady : false,
15733     // private, used to defer height settings to subclasses
15734     deferHeight: false,
15735     /** @cfg {Number} width
15736      * width (optional) size of component
15737      */
15738      /** @cfg {Number} height
15739      * height (optional) size of component
15740      */
15741      
15742     /**
15743      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15744      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15745      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15746      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15747      * @return {Roo.BoxComponent} this
15748      */
15749     setSize : function(w, h){
15750         // support for standard size objects
15751         if(typeof w == 'object'){
15752             h = w.height;
15753             w = w.width;
15754         }
15755         // not rendered
15756         if(!this.boxReady){
15757             this.width = w;
15758             this.height = h;
15759             return this;
15760         }
15761
15762         // prevent recalcs when not needed
15763         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15764             return this;
15765         }
15766         this.lastSize = {width: w, height: h};
15767
15768         var adj = this.adjustSize(w, h);
15769         var aw = adj.width, ah = adj.height;
15770         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15771             var rz = this.getResizeEl();
15772             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15773                 rz.setSize(aw, ah);
15774             }else if(!this.deferHeight && ah !== undefined){
15775                 rz.setHeight(ah);
15776             }else if(aw !== undefined){
15777                 rz.setWidth(aw);
15778             }
15779             this.onResize(aw, ah, w, h);
15780             this.fireEvent('resize', this, aw, ah, w, h);
15781         }
15782         return this;
15783     },
15784
15785     /**
15786      * Gets the current size of the component's underlying element.
15787      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15788      */
15789     getSize : function(){
15790         return this.el.getSize();
15791     },
15792
15793     /**
15794      * Gets the current XY position of the component's underlying element.
15795      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15796      * @return {Array} The XY position of the element (e.g., [100, 200])
15797      */
15798     getPosition : function(local){
15799         if(local === true){
15800             return [this.el.getLeft(true), this.el.getTop(true)];
15801         }
15802         return this.xy || this.el.getXY();
15803     },
15804
15805     /**
15806      * Gets the current box measurements of the component's underlying element.
15807      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15808      * @returns {Object} box An object in the format {x, y, width, height}
15809      */
15810     getBox : function(local){
15811         var s = this.el.getSize();
15812         if(local){
15813             s.x = this.el.getLeft(true);
15814             s.y = this.el.getTop(true);
15815         }else{
15816             var xy = this.xy || this.el.getXY();
15817             s.x = xy[0];
15818             s.y = xy[1];
15819         }
15820         return s;
15821     },
15822
15823     /**
15824      * Sets the current box measurements of the component's underlying element.
15825      * @param {Object} box An object in the format {x, y, width, height}
15826      * @returns {Roo.BoxComponent} this
15827      */
15828     updateBox : function(box){
15829         this.setSize(box.width, box.height);
15830         this.setPagePosition(box.x, box.y);
15831         return this;
15832     },
15833
15834     // protected
15835     getResizeEl : function(){
15836         return this.resizeEl || this.el;
15837     },
15838
15839     // protected
15840     getPositionEl : function(){
15841         return this.positionEl || this.el;
15842     },
15843
15844     /**
15845      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15846      * This method fires the move event.
15847      * @param {Number} left The new left
15848      * @param {Number} top The new top
15849      * @returns {Roo.BoxComponent} this
15850      */
15851     setPosition : function(x, y){
15852         this.x = x;
15853         this.y = y;
15854         if(!this.boxReady){
15855             return this;
15856         }
15857         var adj = this.adjustPosition(x, y);
15858         var ax = adj.x, ay = adj.y;
15859
15860         var el = this.getPositionEl();
15861         if(ax !== undefined || ay !== undefined){
15862             if(ax !== undefined && ay !== undefined){
15863                 el.setLeftTop(ax, ay);
15864             }else if(ax !== undefined){
15865                 el.setLeft(ax);
15866             }else if(ay !== undefined){
15867                 el.setTop(ay);
15868             }
15869             this.onPosition(ax, ay);
15870             this.fireEvent('move', this, ax, ay);
15871         }
15872         return this;
15873     },
15874
15875     /**
15876      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15877      * This method fires the move event.
15878      * @param {Number} x The new x position
15879      * @param {Number} y The new y position
15880      * @returns {Roo.BoxComponent} this
15881      */
15882     setPagePosition : function(x, y){
15883         this.pageX = x;
15884         this.pageY = y;
15885         if(!this.boxReady){
15886             return;
15887         }
15888         if(x === undefined || y === undefined){ // cannot translate undefined points
15889             return;
15890         }
15891         var p = this.el.translatePoints(x, y);
15892         this.setPosition(p.left, p.top);
15893         return this;
15894     },
15895
15896     // private
15897     onRender : function(ct, position){
15898         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15899         if(this.resizeEl){
15900             this.resizeEl = Roo.get(this.resizeEl);
15901         }
15902         if(this.positionEl){
15903             this.positionEl = Roo.get(this.positionEl);
15904         }
15905     },
15906
15907     // private
15908     afterRender : function(){
15909         Roo.BoxComponent.superclass.afterRender.call(this);
15910         this.boxReady = true;
15911         this.setSize(this.width, this.height);
15912         if(this.x || this.y){
15913             this.setPosition(this.x, this.y);
15914         }
15915         if(this.pageX || this.pageY){
15916             this.setPagePosition(this.pageX, this.pageY);
15917         }
15918     },
15919
15920     /**
15921      * Force the component's size to recalculate based on the underlying element's current height and width.
15922      * @returns {Roo.BoxComponent} this
15923      */
15924     syncSize : function(){
15925         delete this.lastSize;
15926         this.setSize(this.el.getWidth(), this.el.getHeight());
15927         return this;
15928     },
15929
15930     /**
15931      * Called after the component is resized, this method is empty by default but can be implemented by any
15932      * subclass that needs to perform custom logic after a resize occurs.
15933      * @param {Number} adjWidth The box-adjusted width that was set
15934      * @param {Number} adjHeight The box-adjusted height that was set
15935      * @param {Number} rawWidth The width that was originally specified
15936      * @param {Number} rawHeight The height that was originally specified
15937      */
15938     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15939
15940     },
15941
15942     /**
15943      * Called after the component is moved, this method is empty by default but can be implemented by any
15944      * subclass that needs to perform custom logic after a move occurs.
15945      * @param {Number} x The new x position
15946      * @param {Number} y The new y position
15947      */
15948     onPosition : function(x, y){
15949
15950     },
15951
15952     // private
15953     adjustSize : function(w, h){
15954         if(this.autoWidth){
15955             w = 'auto';
15956         }
15957         if(this.autoHeight){
15958             h = 'auto';
15959         }
15960         return {width : w, height: h};
15961     },
15962
15963     // private
15964     adjustPosition : function(x, y){
15965         return {x : x, y: y};
15966     }
15967 });/*
15968  * Original code for Roojs - LGPL
15969  * <script type="text/javascript">
15970  */
15971  
15972 /**
15973  * @class Roo.XComponent
15974  * A delayed Element creator...
15975  * Or a way to group chunks of interface together.
15976  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15977  *  used in conjunction with XComponent.build() it will create an instance of each element,
15978  *  then call addxtype() to build the User interface.
15979  * 
15980  * Mypart.xyx = new Roo.XComponent({
15981
15982     parent : 'Mypart.xyz', // empty == document.element.!!
15983     order : '001',
15984     name : 'xxxx'
15985     region : 'xxxx'
15986     disabled : function() {} 
15987      
15988     tree : function() { // return an tree of xtype declared components
15989         var MODULE = this;
15990         return 
15991         {
15992             xtype : 'NestedLayoutPanel',
15993             // technicall
15994         }
15995      ]
15996  *})
15997  *
15998  *
15999  * It can be used to build a big heiracy, with parent etc.
16000  * or you can just use this to render a single compoent to a dom element
16001  * MYPART.render(Roo.Element | String(id) | dom_element )
16002  *
16003  *
16004  * Usage patterns.
16005  *
16006  * Classic Roo
16007  *
16008  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
16009  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
16010  *
16011  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
16012  *
16013  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
16014  * - if mulitple topModules exist, the last one is defined as the top module.
16015  *
16016  * Embeded Roo
16017  * 
16018  * When the top level or multiple modules are to embedded into a existing HTML page,
16019  * the parent element can container '#id' of the element where the module will be drawn.
16020  *
16021  * Bootstrap Roo
16022  *
16023  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
16024  * it relies more on a include mechanism, where sub modules are included into an outer page.
16025  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
16026  * 
16027  * Bootstrap Roo Included elements
16028  *
16029  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
16030  * hence confusing the component builder as it thinks there are multiple top level elements. 
16031  *
16032  * String Over-ride & Translations
16033  *
16034  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
16035  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
16036  * are needed. @see Roo.XComponent.overlayString  
16037  * 
16038  * 
16039  * 
16040  * @extends Roo.util.Observable
16041  * @constructor
16042  * @param cfg {Object} configuration of component
16043  * 
16044  */
16045 Roo.XComponent = function(cfg) {
16046     Roo.apply(this, cfg);
16047     this.addEvents({ 
16048         /**
16049              * @event built
16050              * Fires when this the componnt is built
16051              * @param {Roo.XComponent} c the component
16052              */
16053         'built' : true
16054         
16055     });
16056     this.region = this.region || 'center'; // default..
16057     Roo.XComponent.register(this);
16058     this.modules = false;
16059     this.el = false; // where the layout goes..
16060     
16061     
16062 }
16063 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16064     /**
16065      * @property el
16066      * The created element (with Roo.factory())
16067      * @type {Roo.Layout}
16068      */
16069     el  : false,
16070     
16071     /**
16072      * @property el
16073      * for BC  - use el in new code
16074      * @type {Roo.Layout}
16075      */
16076     panel : false,
16077     
16078     /**
16079      * @property layout
16080      * for BC  - use el in new code
16081      * @type {Roo.Layout}
16082      */
16083     layout : false,
16084     
16085      /**
16086      * @cfg {Function|boolean} disabled
16087      * If this module is disabled by some rule, return true from the funtion
16088      */
16089     disabled : false,
16090     
16091     /**
16092      * @cfg {String} parent 
16093      * Name of parent element which it get xtype added to..
16094      */
16095     parent: false,
16096     
16097     /**
16098      * @cfg {String} order
16099      * Used to set the order in which elements are created (usefull for multiple tabs)
16100      */
16101     
16102     order : false,
16103     /**
16104      * @cfg {String} name
16105      * String to display while loading.
16106      */
16107     name : false,
16108     /**
16109      * @cfg {String} region
16110      * Region to render component to (defaults to center)
16111      */
16112     region : 'center',
16113     
16114     /**
16115      * @cfg {Array} items
16116      * A single item array - the first element is the root of the tree..
16117      * It's done this way to stay compatible with the Xtype system...
16118      */
16119     items : false,
16120     
16121     /**
16122      * @property _tree
16123      * The method that retuns the tree of parts that make up this compoennt 
16124      * @type {function}
16125      */
16126     _tree  : false,
16127     
16128      /**
16129      * render
16130      * render element to dom or tree
16131      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16132      */
16133     
16134     render : function(el)
16135     {
16136         
16137         el = el || false;
16138         var hp = this.parent ? 1 : 0;
16139         Roo.debug &&  Roo.log(this);
16140         
16141         var tree = this._tree ? this._tree() : this.tree();
16142
16143         
16144         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16145             // if parent is a '#.....' string, then let's use that..
16146             var ename = this.parent.substr(1);
16147             this.parent = false;
16148             Roo.debug && Roo.log(ename);
16149             switch (ename) {
16150                 case 'bootstrap-body':
16151                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16152                         // this is the BorderLayout standard?
16153                        this.parent = { el : true };
16154                        break;
16155                     }
16156                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16157                         // need to insert stuff...
16158                         this.parent =  {
16159                              el : new Roo.bootstrap.layout.Border({
16160                                  el : document.body, 
16161                      
16162                                  center: {
16163                                     titlebar: false,
16164                                     autoScroll:false,
16165                                     closeOnTab: true,
16166                                     tabPosition: 'top',
16167                                       //resizeTabs: true,
16168                                     alwaysShowTabs: true,
16169                                     hideTabs: false
16170                                      //minTabWidth: 140
16171                                  }
16172                              })
16173                         
16174                          };
16175                          break;
16176                     }
16177                          
16178                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16179                         this.parent = { el :  new  Roo.bootstrap.Body() };
16180                         Roo.debug && Roo.log("setting el to doc body");
16181                          
16182                     } else {
16183                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16184                     }
16185                     break;
16186                 case 'bootstrap':
16187                     this.parent = { el : true};
16188                     // fall through
16189                 default:
16190                     el = Roo.get(ename);
16191                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16192                         this.parent = { el : true};
16193                     }
16194                     
16195                     break;
16196             }
16197                 
16198             
16199             if (!el && !this.parent) {
16200                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16201                 return;
16202             }
16203         }
16204         
16205         Roo.debug && Roo.log("EL:");
16206         Roo.debug && Roo.log(el);
16207         Roo.debug && Roo.log("this.parent.el:");
16208         Roo.debug && Roo.log(this.parent.el);
16209         
16210
16211         // altertive root elements ??? - we need a better way to indicate these.
16212         var is_alt = Roo.XComponent.is_alt ||
16213                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16214                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16215                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16216         
16217         
16218         
16219         if (!this.parent && is_alt) {
16220             //el = Roo.get(document.body);
16221             this.parent = { el : true };
16222         }
16223             
16224             
16225         
16226         if (!this.parent) {
16227             
16228             Roo.debug && Roo.log("no parent - creating one");
16229             
16230             el = el ? Roo.get(el) : false;      
16231             
16232             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16233                 
16234                 this.parent =  {
16235                     el : new Roo.bootstrap.layout.Border({
16236                         el: el || document.body,
16237                     
16238                         center: {
16239                             titlebar: false,
16240                             autoScroll:false,
16241                             closeOnTab: true,
16242                             tabPosition: 'top',
16243                              //resizeTabs: true,
16244                             alwaysShowTabs: false,
16245                             hideTabs: true,
16246                             minTabWidth: 140,
16247                             overflow: 'visible'
16248                          }
16249                      })
16250                 };
16251             } else {
16252             
16253                 // it's a top level one..
16254                 this.parent =  {
16255                     el : new Roo.BorderLayout(el || document.body, {
16256                         center: {
16257                             titlebar: false,
16258                             autoScroll:false,
16259                             closeOnTab: true,
16260                             tabPosition: 'top',
16261                              //resizeTabs: true,
16262                             alwaysShowTabs: el && hp? false :  true,
16263                             hideTabs: el || !hp ? true :  false,
16264                             minTabWidth: 140
16265                          }
16266                     })
16267                 };
16268             }
16269         }
16270         
16271         if (!this.parent.el) {
16272                 // probably an old style ctor, which has been disabled.
16273                 return;
16274
16275         }
16276                 // The 'tree' method is  '_tree now' 
16277             
16278         tree.region = tree.region || this.region;
16279         var is_body = false;
16280         if (this.parent.el === true) {
16281             // bootstrap... - body..
16282             if (el) {
16283                 tree.el = el;
16284             }
16285             this.parent.el = Roo.factory(tree);
16286             is_body = true;
16287         }
16288         
16289         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16290         this.fireEvent('built', this);
16291         
16292         this.panel = this.el;
16293         this.layout = this.panel.layout;
16294         this.parentLayout = this.parent.layout  || false;  
16295          
16296     }
16297     
16298 });
16299
16300 Roo.apply(Roo.XComponent, {
16301     /**
16302      * @property  hideProgress
16303      * true to disable the building progress bar.. usefull on single page renders.
16304      * @type Boolean
16305      */
16306     hideProgress : false,
16307     /**
16308      * @property  buildCompleted
16309      * True when the builder has completed building the interface.
16310      * @type Boolean
16311      */
16312     buildCompleted : false,
16313      
16314     /**
16315      * @property  topModule
16316      * the upper most module - uses document.element as it's constructor.
16317      * @type Object
16318      */
16319      
16320     topModule  : false,
16321       
16322     /**
16323      * @property  modules
16324      * array of modules to be created by registration system.
16325      * @type {Array} of Roo.XComponent
16326      */
16327     
16328     modules : [],
16329     /**
16330      * @property  elmodules
16331      * array of modules to be created by which use #ID 
16332      * @type {Array} of Roo.XComponent
16333      */
16334      
16335     elmodules : [],
16336
16337      /**
16338      * @property  is_alt
16339      * Is an alternative Root - normally used by bootstrap or other systems,
16340      *    where the top element in the tree can wrap 'body' 
16341      * @type {boolean}  (default false)
16342      */
16343      
16344     is_alt : false,
16345     /**
16346      * @property  build_from_html
16347      * Build elements from html - used by bootstrap HTML stuff 
16348      *    - this is cleared after build is completed
16349      * @type {boolean}    (default false)
16350      */
16351      
16352     build_from_html : false,
16353     /**
16354      * Register components to be built later.
16355      *
16356      * This solves the following issues
16357      * - Building is not done on page load, but after an authentication process has occured.
16358      * - Interface elements are registered on page load
16359      * - Parent Interface elements may not be loaded before child, so this handles that..
16360      * 
16361      *
16362      * example:
16363      * 
16364      * MyApp.register({
16365           order : '000001',
16366           module : 'Pman.Tab.projectMgr',
16367           region : 'center',
16368           parent : 'Pman.layout',
16369           disabled : false,  // or use a function..
16370         })
16371      
16372      * * @param {Object} details about module
16373      */
16374     register : function(obj) {
16375                 
16376         Roo.XComponent.event.fireEvent('register', obj);
16377         switch(typeof(obj.disabled) ) {
16378                 
16379             case 'undefined':
16380                 break;
16381             
16382             case 'function':
16383                 if ( obj.disabled() ) {
16384                         return;
16385                 }
16386                 break;
16387             
16388             default:
16389                 if (obj.disabled) {
16390                         return;
16391                 }
16392                 break;
16393         }
16394                 
16395         this.modules.push(obj);
16396          
16397     },
16398     /**
16399      * convert a string to an object..
16400      * eg. 'AAA.BBB' -> finds AAA.BBB
16401
16402      */
16403     
16404     toObject : function(str)
16405     {
16406         if (!str || typeof(str) == 'object') {
16407             return str;
16408         }
16409         if (str.substring(0,1) == '#') {
16410             return str;
16411         }
16412
16413         var ar = str.split('.');
16414         var rt, o;
16415         rt = ar.shift();
16416             /** eval:var:o */
16417         try {
16418             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16419         } catch (e) {
16420             throw "Module not found : " + str;
16421         }
16422         
16423         if (o === false) {
16424             throw "Module not found : " + str;
16425         }
16426         Roo.each(ar, function(e) {
16427             if (typeof(o[e]) == 'undefined') {
16428                 throw "Module not found : " + str;
16429             }
16430             o = o[e];
16431         });
16432         
16433         return o;
16434         
16435     },
16436     
16437     
16438     /**
16439      * move modules into their correct place in the tree..
16440      * 
16441      */
16442     preBuild : function ()
16443     {
16444         var _t = this;
16445         Roo.each(this.modules , function (obj)
16446         {
16447             Roo.XComponent.event.fireEvent('beforebuild', obj);
16448             
16449             var opar = obj.parent;
16450             try { 
16451                 obj.parent = this.toObject(opar);
16452             } catch(e) {
16453                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16454                 return;
16455             }
16456             
16457             if (!obj.parent) {
16458                 Roo.debug && Roo.log("GOT top level module");
16459                 Roo.debug && Roo.log(obj);
16460                 obj.modules = new Roo.util.MixedCollection(false, 
16461                     function(o) { return o.order + '' }
16462                 );
16463                 this.topModule = obj;
16464                 return;
16465             }
16466                         // parent is a string (usually a dom element name..)
16467             if (typeof(obj.parent) == 'string') {
16468                 this.elmodules.push(obj);
16469                 return;
16470             }
16471             if (obj.parent.constructor != Roo.XComponent) {
16472                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16473             }
16474             if (!obj.parent.modules) {
16475                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16476                     function(o) { return o.order + '' }
16477                 );
16478             }
16479             if (obj.parent.disabled) {
16480                 obj.disabled = true;
16481             }
16482             obj.parent.modules.add(obj);
16483         }, this);
16484     },
16485     
16486      /**
16487      * make a list of modules to build.
16488      * @return {Array} list of modules. 
16489      */ 
16490     
16491     buildOrder : function()
16492     {
16493         var _this = this;
16494         var cmp = function(a,b) {   
16495             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16496         };
16497         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16498             throw "No top level modules to build";
16499         }
16500         
16501         // make a flat list in order of modules to build.
16502         var mods = this.topModule ? [ this.topModule ] : [];
16503                 
16504         
16505         // elmodules (is a list of DOM based modules )
16506         Roo.each(this.elmodules, function(e) {
16507             mods.push(e);
16508             if (!this.topModule &&
16509                 typeof(e.parent) == 'string' &&
16510                 e.parent.substring(0,1) == '#' &&
16511                 Roo.get(e.parent.substr(1))
16512                ) {
16513                 
16514                 _this.topModule = e;
16515             }
16516             
16517         });
16518
16519         
16520         // add modules to their parents..
16521         var addMod = function(m) {
16522             Roo.debug && Roo.log("build Order: add: " + m.name);
16523                 
16524             mods.push(m);
16525             if (m.modules && !m.disabled) {
16526                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16527                 m.modules.keySort('ASC',  cmp );
16528                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16529     
16530                 m.modules.each(addMod);
16531             } else {
16532                 Roo.debug && Roo.log("build Order: no child modules");
16533             }
16534             // not sure if this is used any more..
16535             if (m.finalize) {
16536                 m.finalize.name = m.name + " (clean up) ";
16537                 mods.push(m.finalize);
16538             }
16539             
16540         }
16541         if (this.topModule && this.topModule.modules) { 
16542             this.topModule.modules.keySort('ASC',  cmp );
16543             this.topModule.modules.each(addMod);
16544         } 
16545         return mods;
16546     },
16547     
16548      /**
16549      * Build the registered modules.
16550      * @param {Object} parent element.
16551      * @param {Function} optional method to call after module has been added.
16552      * 
16553      */ 
16554    
16555     build : function(opts) 
16556     {
16557         
16558         if (typeof(opts) != 'undefined') {
16559             Roo.apply(this,opts);
16560         }
16561         
16562         this.preBuild();
16563         var mods = this.buildOrder();
16564       
16565         //this.allmods = mods;
16566         //Roo.debug && Roo.log(mods);
16567         //return;
16568         if (!mods.length) { // should not happen
16569             throw "NO modules!!!";
16570         }
16571         
16572         
16573         var msg = "Building Interface...";
16574         // flash it up as modal - so we store the mask!?
16575         if (!this.hideProgress && Roo.MessageBox) {
16576             Roo.MessageBox.show({ title: 'loading' });
16577             Roo.MessageBox.show({
16578                title: "Please wait...",
16579                msg: msg,
16580                width:450,
16581                progress:true,
16582                closable:false,
16583                modal: false
16584               
16585             });
16586         }
16587         var total = mods.length;
16588         
16589         var _this = this;
16590         var progressRun = function() {
16591             if (!mods.length) {
16592                 Roo.debug && Roo.log('hide?');
16593                 if (!this.hideProgress && Roo.MessageBox) {
16594                     Roo.MessageBox.hide();
16595                 }
16596                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16597                 
16598                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16599                 
16600                 // THE END...
16601                 return false;   
16602             }
16603             
16604             var m = mods.shift();
16605             
16606             
16607             Roo.debug && Roo.log(m);
16608             // not sure if this is supported any more.. - modules that are are just function
16609             if (typeof(m) == 'function') { 
16610                 m.call(this);
16611                 return progressRun.defer(10, _this);
16612             } 
16613             
16614             
16615             msg = "Building Interface " + (total  - mods.length) + 
16616                     " of " + total + 
16617                     (m.name ? (' - ' + m.name) : '');
16618                         Roo.debug && Roo.log(msg);
16619             if (!_this.hideProgress &&  Roo.MessageBox) { 
16620                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16621             }
16622             
16623          
16624             // is the module disabled?
16625             var disabled = (typeof(m.disabled) == 'function') ?
16626                 m.disabled.call(m.module.disabled) : m.disabled;    
16627             
16628             
16629             if (disabled) {
16630                 return progressRun(); // we do not update the display!
16631             }
16632             
16633             // now build 
16634             
16635                         
16636                         
16637             m.render();
16638             // it's 10 on top level, and 1 on others??? why...
16639             return progressRun.defer(10, _this);
16640              
16641         }
16642         progressRun.defer(1, _this);
16643      
16644         
16645         
16646     },
16647     /**
16648      * Overlay a set of modified strings onto a component
16649      * This is dependant on our builder exporting the strings and 'named strings' elements.
16650      * 
16651      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
16652      * @param {Object} associative array of 'named' string and it's new value.
16653      * 
16654      */
16655         overlayStrings : function( component, strings )
16656     {
16657         if (typeof(component['_named_strings']) == 'undefined') {
16658             throw "ERROR: component does not have _named_strings";
16659         }
16660         for ( var k in strings ) {
16661             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
16662             if (md !== false) {
16663                 component['_strings'][md] = strings[k];
16664             } else {
16665                 Roo.log('could not find named string: ' + k + ' in');
16666                 Roo.log(component);
16667             }
16668             
16669         }
16670         
16671     },
16672     
16673         
16674         /**
16675          * Event Object.
16676          *
16677          *
16678          */
16679         event: false, 
16680     /**
16681          * wrapper for event.on - aliased later..  
16682          * Typically use to register a event handler for register:
16683          *
16684          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16685          *
16686          */
16687     on : false
16688    
16689     
16690     
16691 });
16692
16693 Roo.XComponent.event = new Roo.util.Observable({
16694                 events : { 
16695                         /**
16696                          * @event register
16697                          * Fires when an Component is registered,
16698                          * set the disable property on the Component to stop registration.
16699                          * @param {Roo.XComponent} c the component being registerd.
16700                          * 
16701                          */
16702                         'register' : true,
16703             /**
16704                          * @event beforebuild
16705                          * Fires before each Component is built
16706                          * can be used to apply permissions.
16707                          * @param {Roo.XComponent} c the component being registerd.
16708                          * 
16709                          */
16710                         'beforebuild' : true,
16711                         /**
16712                          * @event buildcomplete
16713                          * Fires on the top level element when all elements have been built
16714                          * @param {Roo.XComponent} the top level component.
16715                          */
16716                         'buildcomplete' : true
16717                         
16718                 }
16719 });
16720
16721 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16722  //
16723  /**
16724  * marked - a markdown parser
16725  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
16726  * https://github.com/chjj/marked
16727  */
16728
16729
16730 /**
16731  *
16732  * Roo.Markdown - is a very crude wrapper around marked..
16733  *
16734  * usage:
16735  * 
16736  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
16737  * 
16738  * Note: move the sample code to the bottom of this
16739  * file before uncommenting it.
16740  *
16741  */
16742
16743 Roo.Markdown = {};
16744 Roo.Markdown.toHtml = function(text) {
16745     
16746     var c = new Roo.Markdown.marked.setOptions({
16747             renderer: new Roo.Markdown.marked.Renderer(),
16748             gfm: true,
16749             tables: true,
16750             breaks: false,
16751             pedantic: false,
16752             sanitize: false,
16753             smartLists: true,
16754             smartypants: false
16755           });
16756     // A FEW HACKS!!?
16757     
16758     text = text.replace(/\\\n/g,' ');
16759     return Roo.Markdown.marked(text);
16760 };
16761 //
16762 // converter
16763 //
16764 // Wraps all "globals" so that the only thing
16765 // exposed is makeHtml().
16766 //
16767 (function() {
16768     
16769     /**
16770      * Block-Level Grammar
16771      */
16772     
16773     var block = {
16774       newline: /^\n+/,
16775       code: /^( {4}[^\n]+\n*)+/,
16776       fences: noop,
16777       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
16778       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
16779       nptable: noop,
16780       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
16781       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
16782       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
16783       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
16784       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
16785       table: noop,
16786       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
16787       text: /^[^\n]+/
16788     };
16789     
16790     block.bullet = /(?:[*+-]|\d+\.)/;
16791     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
16792     block.item = replace(block.item, 'gm')
16793       (/bull/g, block.bullet)
16794       ();
16795     
16796     block.list = replace(block.list)
16797       (/bull/g, block.bullet)
16798       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
16799       ('def', '\\n+(?=' + block.def.source + ')')
16800       ();
16801     
16802     block.blockquote = replace(block.blockquote)
16803       ('def', block.def)
16804       ();
16805     
16806     block._tag = '(?!(?:'
16807       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
16808       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
16809       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
16810     
16811     block.html = replace(block.html)
16812       ('comment', /<!--[\s\S]*?-->/)
16813       ('closed', /<(tag)[\s\S]+?<\/\1>/)
16814       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
16815       (/tag/g, block._tag)
16816       ();
16817     
16818     block.paragraph = replace(block.paragraph)
16819       ('hr', block.hr)
16820       ('heading', block.heading)
16821       ('lheading', block.lheading)
16822       ('blockquote', block.blockquote)
16823       ('tag', '<' + block._tag)
16824       ('def', block.def)
16825       ();
16826     
16827     /**
16828      * Normal Block Grammar
16829      */
16830     
16831     block.normal = merge({}, block);
16832     
16833     /**
16834      * GFM Block Grammar
16835      */
16836     
16837     block.gfm = merge({}, block.normal, {
16838       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
16839       paragraph: /^/,
16840       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
16841     });
16842     
16843     block.gfm.paragraph = replace(block.paragraph)
16844       ('(?!', '(?!'
16845         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
16846         + block.list.source.replace('\\1', '\\3') + '|')
16847       ();
16848     
16849     /**
16850      * GFM + Tables Block Grammar
16851      */
16852     
16853     block.tables = merge({}, block.gfm, {
16854       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
16855       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
16856     });
16857     
16858     /**
16859      * Block Lexer
16860      */
16861     
16862     function Lexer(options) {
16863       this.tokens = [];
16864       this.tokens.links = {};
16865       this.options = options || marked.defaults;
16866       this.rules = block.normal;
16867     
16868       if (this.options.gfm) {
16869         if (this.options.tables) {
16870           this.rules = block.tables;
16871         } else {
16872           this.rules = block.gfm;
16873         }
16874       }
16875     }
16876     
16877     /**
16878      * Expose Block Rules
16879      */
16880     
16881     Lexer.rules = block;
16882     
16883     /**
16884      * Static Lex Method
16885      */
16886     
16887     Lexer.lex = function(src, options) {
16888       var lexer = new Lexer(options);
16889       return lexer.lex(src);
16890     };
16891     
16892     /**
16893      * Preprocessing
16894      */
16895     
16896     Lexer.prototype.lex = function(src) {
16897       src = src
16898         .replace(/\r\n|\r/g, '\n')
16899         .replace(/\t/g, '    ')
16900         .replace(/\u00a0/g, ' ')
16901         .replace(/\u2424/g, '\n');
16902     
16903       return this.token(src, true);
16904     };
16905     
16906     /**
16907      * Lexing
16908      */
16909     
16910     Lexer.prototype.token = function(src, top, bq) {
16911       var src = src.replace(/^ +$/gm, '')
16912         , next
16913         , loose
16914         , cap
16915         , bull
16916         , b
16917         , item
16918         , space
16919         , i
16920         , l;
16921     
16922       while (src) {
16923         // newline
16924         if (cap = this.rules.newline.exec(src)) {
16925           src = src.substring(cap[0].length);
16926           if (cap[0].length > 1) {
16927             this.tokens.push({
16928               type: 'space'
16929             });
16930           }
16931         }
16932     
16933         // code
16934         if (cap = this.rules.code.exec(src)) {
16935           src = src.substring(cap[0].length);
16936           cap = cap[0].replace(/^ {4}/gm, '');
16937           this.tokens.push({
16938             type: 'code',
16939             text: !this.options.pedantic
16940               ? cap.replace(/\n+$/, '')
16941               : cap
16942           });
16943           continue;
16944         }
16945     
16946         // fences (gfm)
16947         if (cap = this.rules.fences.exec(src)) {
16948           src = src.substring(cap[0].length);
16949           this.tokens.push({
16950             type: 'code',
16951             lang: cap[2],
16952             text: cap[3] || ''
16953           });
16954           continue;
16955         }
16956     
16957         // heading
16958         if (cap = this.rules.heading.exec(src)) {
16959           src = src.substring(cap[0].length);
16960           this.tokens.push({
16961             type: 'heading',
16962             depth: cap[1].length,
16963             text: cap[2]
16964           });
16965           continue;
16966         }
16967     
16968         // table no leading pipe (gfm)
16969         if (top && (cap = this.rules.nptable.exec(src))) {
16970           src = src.substring(cap[0].length);
16971     
16972           item = {
16973             type: 'table',
16974             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
16975             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
16976             cells: cap[3].replace(/\n$/, '').split('\n')
16977           };
16978     
16979           for (i = 0; i < item.align.length; i++) {
16980             if (/^ *-+: *$/.test(item.align[i])) {
16981               item.align[i] = 'right';
16982             } else if (/^ *:-+: *$/.test(item.align[i])) {
16983               item.align[i] = 'center';
16984             } else if (/^ *:-+ *$/.test(item.align[i])) {
16985               item.align[i] = 'left';
16986             } else {
16987               item.align[i] = null;
16988             }
16989           }
16990     
16991           for (i = 0; i < item.cells.length; i++) {
16992             item.cells[i] = item.cells[i].split(/ *\| */);
16993           }
16994     
16995           this.tokens.push(item);
16996     
16997           continue;
16998         }
16999     
17000         // lheading
17001         if (cap = this.rules.lheading.exec(src)) {
17002           src = src.substring(cap[0].length);
17003           this.tokens.push({
17004             type: 'heading',
17005             depth: cap[2] === '=' ? 1 : 2,
17006             text: cap[1]
17007           });
17008           continue;
17009         }
17010     
17011         // hr
17012         if (cap = this.rules.hr.exec(src)) {
17013           src = src.substring(cap[0].length);
17014           this.tokens.push({
17015             type: 'hr'
17016           });
17017           continue;
17018         }
17019     
17020         // blockquote
17021         if (cap = this.rules.blockquote.exec(src)) {
17022           src = src.substring(cap[0].length);
17023     
17024           this.tokens.push({
17025             type: 'blockquote_start'
17026           });
17027     
17028           cap = cap[0].replace(/^ *> ?/gm, '');
17029     
17030           // Pass `top` to keep the current
17031           // "toplevel" state. This is exactly
17032           // how markdown.pl works.
17033           this.token(cap, top, true);
17034     
17035           this.tokens.push({
17036             type: 'blockquote_end'
17037           });
17038     
17039           continue;
17040         }
17041     
17042         // list
17043         if (cap = this.rules.list.exec(src)) {
17044           src = src.substring(cap[0].length);
17045           bull = cap[2];
17046     
17047           this.tokens.push({
17048             type: 'list_start',
17049             ordered: bull.length > 1
17050           });
17051     
17052           // Get each top-level item.
17053           cap = cap[0].match(this.rules.item);
17054     
17055           next = false;
17056           l = cap.length;
17057           i = 0;
17058     
17059           for (; i < l; i++) {
17060             item = cap[i];
17061     
17062             // Remove the list item's bullet
17063             // so it is seen as the next token.
17064             space = item.length;
17065             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
17066     
17067             // Outdent whatever the
17068             // list item contains. Hacky.
17069             if (~item.indexOf('\n ')) {
17070               space -= item.length;
17071               item = !this.options.pedantic
17072                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
17073                 : item.replace(/^ {1,4}/gm, '');
17074             }
17075     
17076             // Determine whether the next list item belongs here.
17077             // Backpedal if it does not belong in this list.
17078             if (this.options.smartLists && i !== l - 1) {
17079               b = block.bullet.exec(cap[i + 1])[0];
17080               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
17081                 src = cap.slice(i + 1).join('\n') + src;
17082                 i = l - 1;
17083               }
17084             }
17085     
17086             // Determine whether item is loose or not.
17087             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17088             // for discount behavior.
17089             loose = next || /\n\n(?!\s*$)/.test(item);
17090             if (i !== l - 1) {
17091               next = item.charAt(item.length - 1) === '\n';
17092               if (!loose) { loose = next; }
17093             }
17094     
17095             this.tokens.push({
17096               type: loose
17097                 ? 'loose_item_start'
17098                 : 'list_item_start'
17099             });
17100     
17101             // Recurse.
17102             this.token(item, false, bq);
17103     
17104             this.tokens.push({
17105               type: 'list_item_end'
17106             });
17107           }
17108     
17109           this.tokens.push({
17110             type: 'list_end'
17111           });
17112     
17113           continue;
17114         }
17115     
17116         // html
17117         if (cap = this.rules.html.exec(src)) {
17118           src = src.substring(cap[0].length);
17119           this.tokens.push({
17120             type: this.options.sanitize
17121               ? 'paragraph'
17122               : 'html',
17123             pre: !this.options.sanitizer
17124               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17125             text: cap[0]
17126           });
17127           continue;
17128         }
17129     
17130         // def
17131         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17132           src = src.substring(cap[0].length);
17133           this.tokens.links[cap[1].toLowerCase()] = {
17134             href: cap[2],
17135             title: cap[3]
17136           };
17137           continue;
17138         }
17139     
17140         // table (gfm)
17141         if (top && (cap = this.rules.table.exec(src))) {
17142           src = src.substring(cap[0].length);
17143     
17144           item = {
17145             type: 'table',
17146             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17147             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17148             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17149           };
17150     
17151           for (i = 0; i < item.align.length; i++) {
17152             if (/^ *-+: *$/.test(item.align[i])) {
17153               item.align[i] = 'right';
17154             } else if (/^ *:-+: *$/.test(item.align[i])) {
17155               item.align[i] = 'center';
17156             } else if (/^ *:-+ *$/.test(item.align[i])) {
17157               item.align[i] = 'left';
17158             } else {
17159               item.align[i] = null;
17160             }
17161           }
17162     
17163           for (i = 0; i < item.cells.length; i++) {
17164             item.cells[i] = item.cells[i]
17165               .replace(/^ *\| *| *\| *$/g, '')
17166               .split(/ *\| */);
17167           }
17168     
17169           this.tokens.push(item);
17170     
17171           continue;
17172         }
17173     
17174         // top-level paragraph
17175         if (top && (cap = this.rules.paragraph.exec(src))) {
17176           src = src.substring(cap[0].length);
17177           this.tokens.push({
17178             type: 'paragraph',
17179             text: cap[1].charAt(cap[1].length - 1) === '\n'
17180               ? cap[1].slice(0, -1)
17181               : cap[1]
17182           });
17183           continue;
17184         }
17185     
17186         // text
17187         if (cap = this.rules.text.exec(src)) {
17188           // Top-level should never reach here.
17189           src = src.substring(cap[0].length);
17190           this.tokens.push({
17191             type: 'text',
17192             text: cap[0]
17193           });
17194           continue;
17195         }
17196     
17197         if (src) {
17198           throw new
17199             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17200         }
17201       }
17202     
17203       return this.tokens;
17204     };
17205     
17206     /**
17207      * Inline-Level Grammar
17208      */
17209     
17210     var inline = {
17211       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17212       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17213       url: noop,
17214       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17215       link: /^!?\[(inside)\]\(href\)/,
17216       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17217       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17218       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17219       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17220       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17221       br: /^ {2,}\n(?!\s*$)/,
17222       del: noop,
17223       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17224     };
17225     
17226     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17227     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17228     
17229     inline.link = replace(inline.link)
17230       ('inside', inline._inside)
17231       ('href', inline._href)
17232       ();
17233     
17234     inline.reflink = replace(inline.reflink)
17235       ('inside', inline._inside)
17236       ();
17237     
17238     /**
17239      * Normal Inline Grammar
17240      */
17241     
17242     inline.normal = merge({}, inline);
17243     
17244     /**
17245      * Pedantic Inline Grammar
17246      */
17247     
17248     inline.pedantic = merge({}, inline.normal, {
17249       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17250       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17251     });
17252     
17253     /**
17254      * GFM Inline Grammar
17255      */
17256     
17257     inline.gfm = merge({}, inline.normal, {
17258       escape: replace(inline.escape)('])', '~|])')(),
17259       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17260       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17261       text: replace(inline.text)
17262         (']|', '~]|')
17263         ('|', '|https?://|')
17264         ()
17265     });
17266     
17267     /**
17268      * GFM + Line Breaks Inline Grammar
17269      */
17270     
17271     inline.breaks = merge({}, inline.gfm, {
17272       br: replace(inline.br)('{2,}', '*')(),
17273       text: replace(inline.gfm.text)('{2,}', '*')()
17274     });
17275     
17276     /**
17277      * Inline Lexer & Compiler
17278      */
17279     
17280     function InlineLexer(links, options) {
17281       this.options = options || marked.defaults;
17282       this.links = links;
17283       this.rules = inline.normal;
17284       this.renderer = this.options.renderer || new Renderer;
17285       this.renderer.options = this.options;
17286     
17287       if (!this.links) {
17288         throw new
17289           Error('Tokens array requires a `links` property.');
17290       }
17291     
17292       if (this.options.gfm) {
17293         if (this.options.breaks) {
17294           this.rules = inline.breaks;
17295         } else {
17296           this.rules = inline.gfm;
17297         }
17298       } else if (this.options.pedantic) {
17299         this.rules = inline.pedantic;
17300       }
17301     }
17302     
17303     /**
17304      * Expose Inline Rules
17305      */
17306     
17307     InlineLexer.rules = inline;
17308     
17309     /**
17310      * Static Lexing/Compiling Method
17311      */
17312     
17313     InlineLexer.output = function(src, links, options) {
17314       var inline = new InlineLexer(links, options);
17315       return inline.output(src);
17316     };
17317     
17318     /**
17319      * Lexing/Compiling
17320      */
17321     
17322     InlineLexer.prototype.output = function(src) {
17323       var out = ''
17324         , link
17325         , text
17326         , href
17327         , cap;
17328     
17329       while (src) {
17330         // escape
17331         if (cap = this.rules.escape.exec(src)) {
17332           src = src.substring(cap[0].length);
17333           out += cap[1];
17334           continue;
17335         }
17336     
17337         // autolink
17338         if (cap = this.rules.autolink.exec(src)) {
17339           src = src.substring(cap[0].length);
17340           if (cap[2] === '@') {
17341             text = cap[1].charAt(6) === ':'
17342               ? this.mangle(cap[1].substring(7))
17343               : this.mangle(cap[1]);
17344             href = this.mangle('mailto:') + text;
17345           } else {
17346             text = escape(cap[1]);
17347             href = text;
17348           }
17349           out += this.renderer.link(href, null, text);
17350           continue;
17351         }
17352     
17353         // url (gfm)
17354         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17355           src = src.substring(cap[0].length);
17356           text = escape(cap[1]);
17357           href = text;
17358           out += this.renderer.link(href, null, text);
17359           continue;
17360         }
17361     
17362         // tag
17363         if (cap = this.rules.tag.exec(src)) {
17364           if (!this.inLink && /^<a /i.test(cap[0])) {
17365             this.inLink = true;
17366           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17367             this.inLink = false;
17368           }
17369           src = src.substring(cap[0].length);
17370           out += this.options.sanitize
17371             ? this.options.sanitizer
17372               ? this.options.sanitizer(cap[0])
17373               : escape(cap[0])
17374             : cap[0];
17375           continue;
17376         }
17377     
17378         // link
17379         if (cap = this.rules.link.exec(src)) {
17380           src = src.substring(cap[0].length);
17381           this.inLink = true;
17382           out += this.outputLink(cap, {
17383             href: cap[2],
17384             title: cap[3]
17385           });
17386           this.inLink = false;
17387           continue;
17388         }
17389     
17390         // reflink, nolink
17391         if ((cap = this.rules.reflink.exec(src))
17392             || (cap = this.rules.nolink.exec(src))) {
17393           src = src.substring(cap[0].length);
17394           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
17395           link = this.links[link.toLowerCase()];
17396           if (!link || !link.href) {
17397             out += cap[0].charAt(0);
17398             src = cap[0].substring(1) + src;
17399             continue;
17400           }
17401           this.inLink = true;
17402           out += this.outputLink(cap, link);
17403           this.inLink = false;
17404           continue;
17405         }
17406     
17407         // strong
17408         if (cap = this.rules.strong.exec(src)) {
17409           src = src.substring(cap[0].length);
17410           out += this.renderer.strong(this.output(cap[2] || cap[1]));
17411           continue;
17412         }
17413     
17414         // em
17415         if (cap = this.rules.em.exec(src)) {
17416           src = src.substring(cap[0].length);
17417           out += this.renderer.em(this.output(cap[2] || cap[1]));
17418           continue;
17419         }
17420     
17421         // code
17422         if (cap = this.rules.code.exec(src)) {
17423           src = src.substring(cap[0].length);
17424           out += this.renderer.codespan(escape(cap[2], true));
17425           continue;
17426         }
17427     
17428         // br
17429         if (cap = this.rules.br.exec(src)) {
17430           src = src.substring(cap[0].length);
17431           out += this.renderer.br();
17432           continue;
17433         }
17434     
17435         // del (gfm)
17436         if (cap = this.rules.del.exec(src)) {
17437           src = src.substring(cap[0].length);
17438           out += this.renderer.del(this.output(cap[1]));
17439           continue;
17440         }
17441     
17442         // text
17443         if (cap = this.rules.text.exec(src)) {
17444           src = src.substring(cap[0].length);
17445           out += this.renderer.text(escape(this.smartypants(cap[0])));
17446           continue;
17447         }
17448     
17449         if (src) {
17450           throw new
17451             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17452         }
17453       }
17454     
17455       return out;
17456     };
17457     
17458     /**
17459      * Compile Link
17460      */
17461     
17462     InlineLexer.prototype.outputLink = function(cap, link) {
17463       var href = escape(link.href)
17464         , title = link.title ? escape(link.title) : null;
17465     
17466       return cap[0].charAt(0) !== '!'
17467         ? this.renderer.link(href, title, this.output(cap[1]))
17468         : this.renderer.image(href, title, escape(cap[1]));
17469     };
17470     
17471     /**
17472      * Smartypants Transformations
17473      */
17474     
17475     InlineLexer.prototype.smartypants = function(text) {
17476       if (!this.options.smartypants)  { return text; }
17477       return text
17478         // em-dashes
17479         .replace(/---/g, '\u2014')
17480         // en-dashes
17481         .replace(/--/g, '\u2013')
17482         // opening singles
17483         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
17484         // closing singles & apostrophes
17485         .replace(/'/g, '\u2019')
17486         // opening doubles
17487         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
17488         // closing doubles
17489         .replace(/"/g, '\u201d')
17490         // ellipses
17491         .replace(/\.{3}/g, '\u2026');
17492     };
17493     
17494     /**
17495      * Mangle Links
17496      */
17497     
17498     InlineLexer.prototype.mangle = function(text) {
17499       if (!this.options.mangle) { return text; }
17500       var out = ''
17501         , l = text.length
17502         , i = 0
17503         , ch;
17504     
17505       for (; i < l; i++) {
17506         ch = text.charCodeAt(i);
17507         if (Math.random() > 0.5) {
17508           ch = 'x' + ch.toString(16);
17509         }
17510         out += '&#' + ch + ';';
17511       }
17512     
17513       return out;
17514     };
17515     
17516     /**
17517      * Renderer
17518      */
17519     
17520     function Renderer(options) {
17521       this.options = options || {};
17522     }
17523     
17524     Renderer.prototype.code = function(code, lang, escaped) {
17525       if (this.options.highlight) {
17526         var out = this.options.highlight(code, lang);
17527         if (out != null && out !== code) {
17528           escaped = true;
17529           code = out;
17530         }
17531       } else {
17532             // hack!!! - it's already escapeD?
17533             escaped = true;
17534       }
17535     
17536       if (!lang) {
17537         return '<pre><code>'
17538           + (escaped ? code : escape(code, true))
17539           + '\n</code></pre>';
17540       }
17541     
17542       return '<pre><code class="'
17543         + this.options.langPrefix
17544         + escape(lang, true)
17545         + '">'
17546         + (escaped ? code : escape(code, true))
17547         + '\n</code></pre>\n';
17548     };
17549     
17550     Renderer.prototype.blockquote = function(quote) {
17551       return '<blockquote>\n' + quote + '</blockquote>\n';
17552     };
17553     
17554     Renderer.prototype.html = function(html) {
17555       return html;
17556     };
17557     
17558     Renderer.prototype.heading = function(text, level, raw) {
17559       return '<h'
17560         + level
17561         + ' id="'
17562         + this.options.headerPrefix
17563         + raw.toLowerCase().replace(/[^\w]+/g, '-')
17564         + '">'
17565         + text
17566         + '</h'
17567         + level
17568         + '>\n';
17569     };
17570     
17571     Renderer.prototype.hr = function() {
17572       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
17573     };
17574     
17575     Renderer.prototype.list = function(body, ordered) {
17576       var type = ordered ? 'ol' : 'ul';
17577       return '<' + type + '>\n' + body + '</' + type + '>\n';
17578     };
17579     
17580     Renderer.prototype.listitem = function(text) {
17581       return '<li>' + text + '</li>\n';
17582     };
17583     
17584     Renderer.prototype.paragraph = function(text) {
17585       return '<p>' + text + '</p>\n';
17586     };
17587     
17588     Renderer.prototype.table = function(header, body) {
17589       return '<table class="table table-striped">\n'
17590         + '<thead>\n'
17591         + header
17592         + '</thead>\n'
17593         + '<tbody>\n'
17594         + body
17595         + '</tbody>\n'
17596         + '</table>\n';
17597     };
17598     
17599     Renderer.prototype.tablerow = function(content) {
17600       return '<tr>\n' + content + '</tr>\n';
17601     };
17602     
17603     Renderer.prototype.tablecell = function(content, flags) {
17604       var type = flags.header ? 'th' : 'td';
17605       var tag = flags.align
17606         ? '<' + type + ' style="text-align:' + flags.align + '">'
17607         : '<' + type + '>';
17608       return tag + content + '</' + type + '>\n';
17609     };
17610     
17611     // span level renderer
17612     Renderer.prototype.strong = function(text) {
17613       return '<strong>' + text + '</strong>';
17614     };
17615     
17616     Renderer.prototype.em = function(text) {
17617       return '<em>' + text + '</em>';
17618     };
17619     
17620     Renderer.prototype.codespan = function(text) {
17621       return '<code>' + text + '</code>';
17622     };
17623     
17624     Renderer.prototype.br = function() {
17625       return this.options.xhtml ? '<br/>' : '<br>';
17626     };
17627     
17628     Renderer.prototype.del = function(text) {
17629       return '<del>' + text + '</del>';
17630     };
17631     
17632     Renderer.prototype.link = function(href, title, text) {
17633       if (this.options.sanitize) {
17634         try {
17635           var prot = decodeURIComponent(unescape(href))
17636             .replace(/[^\w:]/g, '')
17637             .toLowerCase();
17638         } catch (e) {
17639           return '';
17640         }
17641         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
17642           return '';
17643         }
17644       }
17645       var out = '<a href="' + href + '"';
17646       if (title) {
17647         out += ' title="' + title + '"';
17648       }
17649       out += '>' + text + '</a>';
17650       return out;
17651     };
17652     
17653     Renderer.prototype.image = function(href, title, text) {
17654       var out = '<img src="' + href + '" alt="' + text + '"';
17655       if (title) {
17656         out += ' title="' + title + '"';
17657       }
17658       out += this.options.xhtml ? '/>' : '>';
17659       return out;
17660     };
17661     
17662     Renderer.prototype.text = function(text) {
17663       return text;
17664     };
17665     
17666     /**
17667      * Parsing & Compiling
17668      */
17669     
17670     function Parser(options) {
17671       this.tokens = [];
17672       this.token = null;
17673       this.options = options || marked.defaults;
17674       this.options.renderer = this.options.renderer || new Renderer;
17675       this.renderer = this.options.renderer;
17676       this.renderer.options = this.options;
17677     }
17678     
17679     /**
17680      * Static Parse Method
17681      */
17682     
17683     Parser.parse = function(src, options, renderer) {
17684       var parser = new Parser(options, renderer);
17685       return parser.parse(src);
17686     };
17687     
17688     /**
17689      * Parse Loop
17690      */
17691     
17692     Parser.prototype.parse = function(src) {
17693       this.inline = new InlineLexer(src.links, this.options, this.renderer);
17694       this.tokens = src.reverse();
17695     
17696       var out = '';
17697       while (this.next()) {
17698         out += this.tok();
17699       }
17700     
17701       return out;
17702     };
17703     
17704     /**
17705      * Next Token
17706      */
17707     
17708     Parser.prototype.next = function() {
17709       return this.token = this.tokens.pop();
17710     };
17711     
17712     /**
17713      * Preview Next Token
17714      */
17715     
17716     Parser.prototype.peek = function() {
17717       return this.tokens[this.tokens.length - 1] || 0;
17718     };
17719     
17720     /**
17721      * Parse Text Tokens
17722      */
17723     
17724     Parser.prototype.parseText = function() {
17725       var body = this.token.text;
17726     
17727       while (this.peek().type === 'text') {
17728         body += '\n' + this.next().text;
17729       }
17730     
17731       return this.inline.output(body);
17732     };
17733     
17734     /**
17735      * Parse Current Token
17736      */
17737     
17738     Parser.prototype.tok = function() {
17739       switch (this.token.type) {
17740         case 'space': {
17741           return '';
17742         }
17743         case 'hr': {
17744           return this.renderer.hr();
17745         }
17746         case 'heading': {
17747           return this.renderer.heading(
17748             this.inline.output(this.token.text),
17749             this.token.depth,
17750             this.token.text);
17751         }
17752         case 'code': {
17753           return this.renderer.code(this.token.text,
17754             this.token.lang,
17755             this.token.escaped);
17756         }
17757         case 'table': {
17758           var header = ''
17759             , body = ''
17760             , i
17761             , row
17762             , cell
17763             , flags
17764             , j;
17765     
17766           // header
17767           cell = '';
17768           for (i = 0; i < this.token.header.length; i++) {
17769             flags = { header: true, align: this.token.align[i] };
17770             cell += this.renderer.tablecell(
17771               this.inline.output(this.token.header[i]),
17772               { header: true, align: this.token.align[i] }
17773             );
17774           }
17775           header += this.renderer.tablerow(cell);
17776     
17777           for (i = 0; i < this.token.cells.length; i++) {
17778             row = this.token.cells[i];
17779     
17780             cell = '';
17781             for (j = 0; j < row.length; j++) {
17782               cell += this.renderer.tablecell(
17783                 this.inline.output(row[j]),
17784                 { header: false, align: this.token.align[j] }
17785               );
17786             }
17787     
17788             body += this.renderer.tablerow(cell);
17789           }
17790           return this.renderer.table(header, body);
17791         }
17792         case 'blockquote_start': {
17793           var body = '';
17794     
17795           while (this.next().type !== 'blockquote_end') {
17796             body += this.tok();
17797           }
17798     
17799           return this.renderer.blockquote(body);
17800         }
17801         case 'list_start': {
17802           var body = ''
17803             , ordered = this.token.ordered;
17804     
17805           while (this.next().type !== 'list_end') {
17806             body += this.tok();
17807           }
17808     
17809           return this.renderer.list(body, ordered);
17810         }
17811         case 'list_item_start': {
17812           var body = '';
17813     
17814           while (this.next().type !== 'list_item_end') {
17815             body += this.token.type === 'text'
17816               ? this.parseText()
17817               : this.tok();
17818           }
17819     
17820           return this.renderer.listitem(body);
17821         }
17822         case 'loose_item_start': {
17823           var body = '';
17824     
17825           while (this.next().type !== 'list_item_end') {
17826             body += this.tok();
17827           }
17828     
17829           return this.renderer.listitem(body);
17830         }
17831         case 'html': {
17832           var html = !this.token.pre && !this.options.pedantic
17833             ? this.inline.output(this.token.text)
17834             : this.token.text;
17835           return this.renderer.html(html);
17836         }
17837         case 'paragraph': {
17838           return this.renderer.paragraph(this.inline.output(this.token.text));
17839         }
17840         case 'text': {
17841           return this.renderer.paragraph(this.parseText());
17842         }
17843       }
17844     };
17845     
17846     /**
17847      * Helpers
17848      */
17849     
17850     function escape(html, encode) {
17851       return html
17852         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17853         .replace(/</g, '&lt;')
17854         .replace(/>/g, '&gt;')
17855         .replace(/"/g, '&quot;')
17856         .replace(/'/g, '&#39;');
17857     }
17858     
17859     function unescape(html) {
17860         // explicitly match decimal, hex, and named HTML entities 
17861       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17862         n = n.toLowerCase();
17863         if (n === 'colon') { return ':'; }
17864         if (n.charAt(0) === '#') {
17865           return n.charAt(1) === 'x'
17866             ? String.fromCharCode(parseInt(n.substring(2), 16))
17867             : String.fromCharCode(+n.substring(1));
17868         }
17869         return '';
17870       });
17871     }
17872     
17873     function replace(regex, opt) {
17874       regex = regex.source;
17875       opt = opt || '';
17876       return function self(name, val) {
17877         if (!name) { return new RegExp(regex, opt); }
17878         val = val.source || val;
17879         val = val.replace(/(^|[^\[])\^/g, '$1');
17880         regex = regex.replace(name, val);
17881         return self;
17882       };
17883     }
17884     
17885     function noop() {}
17886     noop.exec = noop;
17887     
17888     function merge(obj) {
17889       var i = 1
17890         , target
17891         , key;
17892     
17893       for (; i < arguments.length; i++) {
17894         target = arguments[i];
17895         for (key in target) {
17896           if (Object.prototype.hasOwnProperty.call(target, key)) {
17897             obj[key] = target[key];
17898           }
17899         }
17900       }
17901     
17902       return obj;
17903     }
17904     
17905     
17906     /**
17907      * Marked
17908      */
17909     
17910     function marked(src, opt, callback) {
17911       if (callback || typeof opt === 'function') {
17912         if (!callback) {
17913           callback = opt;
17914           opt = null;
17915         }
17916     
17917         opt = merge({}, marked.defaults, opt || {});
17918     
17919         var highlight = opt.highlight
17920           , tokens
17921           , pending
17922           , i = 0;
17923     
17924         try {
17925           tokens = Lexer.lex(src, opt)
17926         } catch (e) {
17927           return callback(e);
17928         }
17929     
17930         pending = tokens.length;
17931     
17932         var done = function(err) {
17933           if (err) {
17934             opt.highlight = highlight;
17935             return callback(err);
17936           }
17937     
17938           var out;
17939     
17940           try {
17941             out = Parser.parse(tokens, opt);
17942           } catch (e) {
17943             err = e;
17944           }
17945     
17946           opt.highlight = highlight;
17947     
17948           return err
17949             ? callback(err)
17950             : callback(null, out);
17951         };
17952     
17953         if (!highlight || highlight.length < 3) {
17954           return done();
17955         }
17956     
17957         delete opt.highlight;
17958     
17959         if (!pending) { return done(); }
17960     
17961         for (; i < tokens.length; i++) {
17962           (function(token) {
17963             if (token.type !== 'code') {
17964               return --pending || done();
17965             }
17966             return highlight(token.text, token.lang, function(err, code) {
17967               if (err) { return done(err); }
17968               if (code == null || code === token.text) {
17969                 return --pending || done();
17970               }
17971               token.text = code;
17972               token.escaped = true;
17973               --pending || done();
17974             });
17975           })(tokens[i]);
17976         }
17977     
17978         return;
17979       }
17980       try {
17981         if (opt) { opt = merge({}, marked.defaults, opt); }
17982         return Parser.parse(Lexer.lex(src, opt), opt);
17983       } catch (e) {
17984         e.message += '\nPlease report this to https://github.com/chjj/marked.';
17985         if ((opt || marked.defaults).silent) {
17986           return '<p>An error occured:</p><pre>'
17987             + escape(e.message + '', true)
17988             + '</pre>';
17989         }
17990         throw e;
17991       }
17992     }
17993     
17994     /**
17995      * Options
17996      */
17997     
17998     marked.options =
17999     marked.setOptions = function(opt) {
18000       merge(marked.defaults, opt);
18001       return marked;
18002     };
18003     
18004     marked.defaults = {
18005       gfm: true,
18006       tables: true,
18007       breaks: false,
18008       pedantic: false,
18009       sanitize: false,
18010       sanitizer: null,
18011       mangle: true,
18012       smartLists: false,
18013       silent: false,
18014       highlight: null,
18015       langPrefix: 'lang-',
18016       smartypants: false,
18017       headerPrefix: '',
18018       renderer: new Renderer,
18019       xhtml: false
18020     };
18021     
18022     /**
18023      * Expose
18024      */
18025     
18026     marked.Parser = Parser;
18027     marked.parser = Parser.parse;
18028     
18029     marked.Renderer = Renderer;
18030     
18031     marked.Lexer = Lexer;
18032     marked.lexer = Lexer.lex;
18033     
18034     marked.InlineLexer = InlineLexer;
18035     marked.inlineLexer = InlineLexer.output;
18036     
18037     marked.parse = marked;
18038     
18039     Roo.Markdown.marked = marked;
18040
18041 })();/*
18042  * Based on:
18043  * Ext JS Library 1.1.1
18044  * Copyright(c) 2006-2007, Ext JS, LLC.
18045  *
18046  * Originally Released Under LGPL - original licence link has changed is not relivant.
18047  *
18048  * Fork - LGPL
18049  * <script type="text/javascript">
18050  */
18051
18052
18053
18054 /*
18055  * These classes are derivatives of the similarly named classes in the YUI Library.
18056  * The original license:
18057  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18058  * Code licensed under the BSD License:
18059  * http://developer.yahoo.net/yui/license.txt
18060  */
18061
18062 (function() {
18063
18064 var Event=Roo.EventManager;
18065 var Dom=Roo.lib.Dom;
18066
18067 /**
18068  * @class Roo.dd.DragDrop
18069  * @extends Roo.util.Observable
18070  * Defines the interface and base operation of items that that can be
18071  * dragged or can be drop targets.  It was designed to be extended, overriding
18072  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
18073  * Up to three html elements can be associated with a DragDrop instance:
18074  * <ul>
18075  * <li>linked element: the element that is passed into the constructor.
18076  * This is the element which defines the boundaries for interaction with
18077  * other DragDrop objects.</li>
18078  * <li>handle element(s): The drag operation only occurs if the element that
18079  * was clicked matches a handle element.  By default this is the linked
18080  * element, but there are times that you will want only a portion of the
18081  * linked element to initiate the drag operation, and the setHandleElId()
18082  * method provides a way to define this.</li>
18083  * <li>drag element: this represents the element that would be moved along
18084  * with the cursor during a drag operation.  By default, this is the linked
18085  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18086  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18087  * </li>
18088  * </ul>
18089  * This class should not be instantiated until the onload event to ensure that
18090  * the associated elements are available.
18091  * The following would define a DragDrop obj that would interact with any
18092  * other DragDrop obj in the "group1" group:
18093  * <pre>
18094  *  dd = new Roo.dd.DragDrop("div1", "group1");
18095  * </pre>
18096  * Since none of the event handlers have been implemented, nothing would
18097  * actually happen if you were to run the code above.  Normally you would
18098  * override this class or one of the default implementations, but you can
18099  * also override the methods you want on an instance of the class...
18100  * <pre>
18101  *  dd.onDragDrop = function(e, id) {
18102  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18103  *  }
18104  * </pre>
18105  * @constructor
18106  * @param {String} id of the element that is linked to this instance
18107  * @param {String} sGroup the group of related DragDrop objects
18108  * @param {object} config an object containing configurable attributes
18109  *                Valid properties for DragDrop:
18110  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18111  */
18112 Roo.dd.DragDrop = function(id, sGroup, config) {
18113     if (id) {
18114         this.init(id, sGroup, config);
18115     }
18116     
18117 };
18118
18119 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18120
18121     /**
18122      * The id of the element associated with this object.  This is what we
18123      * refer to as the "linked element" because the size and position of
18124      * this element is used to determine when the drag and drop objects have
18125      * interacted.
18126      * @property id
18127      * @type String
18128      */
18129     id: null,
18130
18131     /**
18132      * Configuration attributes passed into the constructor
18133      * @property config
18134      * @type object
18135      */
18136     config: null,
18137
18138     /**
18139      * The id of the element that will be dragged.  By default this is same
18140      * as the linked element , but could be changed to another element. Ex:
18141      * Roo.dd.DDProxy
18142      * @property dragElId
18143      * @type String
18144      * @private
18145      */
18146     dragElId: null,
18147
18148     /**
18149      * the id of the element that initiates the drag operation.  By default
18150      * this is the linked element, but could be changed to be a child of this
18151      * element.  This lets us do things like only starting the drag when the
18152      * header element within the linked html element is clicked.
18153      * @property handleElId
18154      * @type String
18155      * @private
18156      */
18157     handleElId: null,
18158
18159     /**
18160      * An associative array of HTML tags that will be ignored if clicked.
18161      * @property invalidHandleTypes
18162      * @type {string: string}
18163      */
18164     invalidHandleTypes: null,
18165
18166     /**
18167      * An associative array of ids for elements that will be ignored if clicked
18168      * @property invalidHandleIds
18169      * @type {string: string}
18170      */
18171     invalidHandleIds: null,
18172
18173     /**
18174      * An indexted array of css class names for elements that will be ignored
18175      * if clicked.
18176      * @property invalidHandleClasses
18177      * @type string[]
18178      */
18179     invalidHandleClasses: null,
18180
18181     /**
18182      * The linked element's absolute X position at the time the drag was
18183      * started
18184      * @property startPageX
18185      * @type int
18186      * @private
18187      */
18188     startPageX: 0,
18189
18190     /**
18191      * The linked element's absolute X position at the time the drag was
18192      * started
18193      * @property startPageY
18194      * @type int
18195      * @private
18196      */
18197     startPageY: 0,
18198
18199     /**
18200      * The group defines a logical collection of DragDrop objects that are
18201      * related.  Instances only get events when interacting with other
18202      * DragDrop object in the same group.  This lets us define multiple
18203      * groups using a single DragDrop subclass if we want.
18204      * @property groups
18205      * @type {string: string}
18206      */
18207     groups: null,
18208
18209     /**
18210      * Individual drag/drop instances can be locked.  This will prevent
18211      * onmousedown start drag.
18212      * @property locked
18213      * @type boolean
18214      * @private
18215      */
18216     locked: false,
18217
18218     /**
18219      * Lock this instance
18220      * @method lock
18221      */
18222     lock: function() { this.locked = true; },
18223
18224     /**
18225      * Unlock this instace
18226      * @method unlock
18227      */
18228     unlock: function() { this.locked = false; },
18229
18230     /**
18231      * By default, all insances can be a drop target.  This can be disabled by
18232      * setting isTarget to false.
18233      * @method isTarget
18234      * @type boolean
18235      */
18236     isTarget: true,
18237
18238     /**
18239      * The padding configured for this drag and drop object for calculating
18240      * the drop zone intersection with this object.
18241      * @method padding
18242      * @type int[]
18243      */
18244     padding: null,
18245
18246     /**
18247      * Cached reference to the linked element
18248      * @property _domRef
18249      * @private
18250      */
18251     _domRef: null,
18252
18253     /**
18254      * Internal typeof flag
18255      * @property __ygDragDrop
18256      * @private
18257      */
18258     __ygDragDrop: true,
18259
18260     /**
18261      * Set to true when horizontal contraints are applied
18262      * @property constrainX
18263      * @type boolean
18264      * @private
18265      */
18266     constrainX: false,
18267
18268     /**
18269      * Set to true when vertical contraints are applied
18270      * @property constrainY
18271      * @type boolean
18272      * @private
18273      */
18274     constrainY: false,
18275
18276     /**
18277      * The left constraint
18278      * @property minX
18279      * @type int
18280      * @private
18281      */
18282     minX: 0,
18283
18284     /**
18285      * The right constraint
18286      * @property maxX
18287      * @type int
18288      * @private
18289      */
18290     maxX: 0,
18291
18292     /**
18293      * The up constraint
18294      * @property minY
18295      * @type int
18296      * @type int
18297      * @private
18298      */
18299     minY: 0,
18300
18301     /**
18302      * The down constraint
18303      * @property maxY
18304      * @type int
18305      * @private
18306      */
18307     maxY: 0,
18308
18309     /**
18310      * Maintain offsets when we resetconstraints.  Set to true when you want
18311      * the position of the element relative to its parent to stay the same
18312      * when the page changes
18313      *
18314      * @property maintainOffset
18315      * @type boolean
18316      */
18317     maintainOffset: false,
18318
18319     /**
18320      * Array of pixel locations the element will snap to if we specified a
18321      * horizontal graduation/interval.  This array is generated automatically
18322      * when you define a tick interval.
18323      * @property xTicks
18324      * @type int[]
18325      */
18326     xTicks: null,
18327
18328     /**
18329      * Array of pixel locations the element will snap to if we specified a
18330      * vertical graduation/interval.  This array is generated automatically
18331      * when you define a tick interval.
18332      * @property yTicks
18333      * @type int[]
18334      */
18335     yTicks: null,
18336
18337     /**
18338      * By default the drag and drop instance will only respond to the primary
18339      * button click (left button for a right-handed mouse).  Set to true to
18340      * allow drag and drop to start with any mouse click that is propogated
18341      * by the browser
18342      * @property primaryButtonOnly
18343      * @type boolean
18344      */
18345     primaryButtonOnly: true,
18346
18347     /**
18348      * The availabe property is false until the linked dom element is accessible.
18349      * @property available
18350      * @type boolean
18351      */
18352     available: false,
18353
18354     /**
18355      * By default, drags can only be initiated if the mousedown occurs in the
18356      * region the linked element is.  This is done in part to work around a
18357      * bug in some browsers that mis-report the mousedown if the previous
18358      * mouseup happened outside of the window.  This property is set to true
18359      * if outer handles are defined.
18360      *
18361      * @property hasOuterHandles
18362      * @type boolean
18363      * @default false
18364      */
18365     hasOuterHandles: false,
18366
18367     /**
18368      * Code that executes immediately before the startDrag event
18369      * @method b4StartDrag
18370      * @private
18371      */
18372     b4StartDrag: function(x, y) { },
18373
18374     /**
18375      * Abstract method called after a drag/drop object is clicked
18376      * and the drag or mousedown time thresholds have beeen met.
18377      * @method startDrag
18378      * @param {int} X click location
18379      * @param {int} Y click location
18380      */
18381     startDrag: function(x, y) { /* override this */ },
18382
18383     /**
18384      * Code that executes immediately before the onDrag event
18385      * @method b4Drag
18386      * @private
18387      */
18388     b4Drag: function(e) { },
18389
18390     /**
18391      * Abstract method called during the onMouseMove event while dragging an
18392      * object.
18393      * @method onDrag
18394      * @param {Event} e the mousemove event
18395      */
18396     onDrag: function(e) { /* override this */ },
18397
18398     /**
18399      * Abstract method called when this element fist begins hovering over
18400      * another DragDrop obj
18401      * @method onDragEnter
18402      * @param {Event} e the mousemove event
18403      * @param {String|DragDrop[]} id In POINT mode, the element
18404      * id this is hovering over.  In INTERSECT mode, an array of one or more
18405      * dragdrop items being hovered over.
18406      */
18407     onDragEnter: function(e, id) { /* override this */ },
18408
18409     /**
18410      * Code that executes immediately before the onDragOver event
18411      * @method b4DragOver
18412      * @private
18413      */
18414     b4DragOver: function(e) { },
18415
18416     /**
18417      * Abstract method called when this element is hovering over another
18418      * DragDrop obj
18419      * @method onDragOver
18420      * @param {Event} e the mousemove event
18421      * @param {String|DragDrop[]} id In POINT mode, the element
18422      * id this is hovering over.  In INTERSECT mode, an array of dd items
18423      * being hovered over.
18424      */
18425     onDragOver: function(e, id) { /* override this */ },
18426
18427     /**
18428      * Code that executes immediately before the onDragOut event
18429      * @method b4DragOut
18430      * @private
18431      */
18432     b4DragOut: function(e) { },
18433
18434     /**
18435      * Abstract method called when we are no longer hovering over an element
18436      * @method onDragOut
18437      * @param {Event} e the mousemove event
18438      * @param {String|DragDrop[]} id In POINT mode, the element
18439      * id this was hovering over.  In INTERSECT mode, an array of dd items
18440      * that the mouse is no longer over.
18441      */
18442     onDragOut: function(e, id) { /* override this */ },
18443
18444     /**
18445      * Code that executes immediately before the onDragDrop event
18446      * @method b4DragDrop
18447      * @private
18448      */
18449     b4DragDrop: function(e) { },
18450
18451     /**
18452      * Abstract method called when this item is dropped on another DragDrop
18453      * obj
18454      * @method onDragDrop
18455      * @param {Event} e the mouseup event
18456      * @param {String|DragDrop[]} id In POINT mode, the element
18457      * id this was dropped on.  In INTERSECT mode, an array of dd items this
18458      * was dropped on.
18459      */
18460     onDragDrop: function(e, id) { /* override this */ },
18461
18462     /**
18463      * Abstract method called when this item is dropped on an area with no
18464      * drop target
18465      * @method onInvalidDrop
18466      * @param {Event} e the mouseup event
18467      */
18468     onInvalidDrop: function(e) { /* override this */ },
18469
18470     /**
18471      * Code that executes immediately before the endDrag event
18472      * @method b4EndDrag
18473      * @private
18474      */
18475     b4EndDrag: function(e) { },
18476
18477     /**
18478      * Fired when we are done dragging the object
18479      * @method endDrag
18480      * @param {Event} e the mouseup event
18481      */
18482     endDrag: function(e) { /* override this */ },
18483
18484     /**
18485      * Code executed immediately before the onMouseDown event
18486      * @method b4MouseDown
18487      * @param {Event} e the mousedown event
18488      * @private
18489      */
18490     b4MouseDown: function(e) {  },
18491
18492     /**
18493      * Event handler that fires when a drag/drop obj gets a mousedown
18494      * @method onMouseDown
18495      * @param {Event} e the mousedown event
18496      */
18497     onMouseDown: function(e) { /* override this */ },
18498
18499     /**
18500      * Event handler that fires when a drag/drop obj gets a mouseup
18501      * @method onMouseUp
18502      * @param {Event} e the mouseup event
18503      */
18504     onMouseUp: function(e) { /* override this */ },
18505
18506     /**
18507      * Override the onAvailable method to do what is needed after the initial
18508      * position was determined.
18509      * @method onAvailable
18510      */
18511     onAvailable: function () {
18512     },
18513
18514     /*
18515      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
18516      * @type Object
18517      */
18518     defaultPadding : {left:0, right:0, top:0, bottom:0},
18519
18520     /*
18521      * Initializes the drag drop object's constraints to restrict movement to a certain element.
18522  *
18523  * Usage:
18524  <pre><code>
18525  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
18526                 { dragElId: "existingProxyDiv" });
18527  dd.startDrag = function(){
18528      this.constrainTo("parent-id");
18529  };
18530  </code></pre>
18531  * Or you can initalize it using the {@link Roo.Element} object:
18532  <pre><code>
18533  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
18534      startDrag : function(){
18535          this.constrainTo("parent-id");
18536      }
18537  });
18538  </code></pre>
18539      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
18540      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
18541      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
18542      * an object containing the sides to pad. For example: {right:10, bottom:10}
18543      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
18544      */
18545     constrainTo : function(constrainTo, pad, inContent){
18546         if(typeof pad == "number"){
18547             pad = {left: pad, right:pad, top:pad, bottom:pad};
18548         }
18549         pad = pad || this.defaultPadding;
18550         var b = Roo.get(this.getEl()).getBox();
18551         var ce = Roo.get(constrainTo);
18552         var s = ce.getScroll();
18553         var c, cd = ce.dom;
18554         if(cd == document.body){
18555             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
18556         }else{
18557             xy = ce.getXY();
18558             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
18559         }
18560
18561
18562         var topSpace = b.y - c.y;
18563         var leftSpace = b.x - c.x;
18564
18565         this.resetConstraints();
18566         this.setXConstraint(leftSpace - (pad.left||0), // left
18567                 c.width - leftSpace - b.width - (pad.right||0) //right
18568         );
18569         this.setYConstraint(topSpace - (pad.top||0), //top
18570                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
18571         );
18572     },
18573
18574     /**
18575      * Returns a reference to the linked element
18576      * @method getEl
18577      * @return {HTMLElement} the html element
18578      */
18579     getEl: function() {
18580         if (!this._domRef) {
18581             this._domRef = Roo.getDom(this.id);
18582         }
18583
18584         return this._domRef;
18585     },
18586
18587     /**
18588      * Returns a reference to the actual element to drag.  By default this is
18589      * the same as the html element, but it can be assigned to another
18590      * element. An example of this can be found in Roo.dd.DDProxy
18591      * @method getDragEl
18592      * @return {HTMLElement} the html element
18593      */
18594     getDragEl: function() {
18595         return Roo.getDom(this.dragElId);
18596     },
18597
18598     /**
18599      * Sets up the DragDrop object.  Must be called in the constructor of any
18600      * Roo.dd.DragDrop subclass
18601      * @method init
18602      * @param id the id of the linked element
18603      * @param {String} sGroup the group of related items
18604      * @param {object} config configuration attributes
18605      */
18606     init: function(id, sGroup, config) {
18607         this.initTarget(id, sGroup, config);
18608         if (!Roo.isTouch) {
18609             Event.on(this.id, "mousedown", this.handleMouseDown, this);
18610         }
18611         Event.on(this.id, "touchstart", this.handleMouseDown, this);
18612         // Event.on(this.id, "selectstart", Event.preventDefault);
18613     },
18614
18615     /**
18616      * Initializes Targeting functionality only... the object does not
18617      * get a mousedown handler.
18618      * @method initTarget
18619      * @param id the id of the linked element
18620      * @param {String} sGroup the group of related items
18621      * @param {object} config configuration attributes
18622      */
18623     initTarget: function(id, sGroup, config) {
18624
18625         // configuration attributes
18626         this.config = config || {};
18627
18628         // create a local reference to the drag and drop manager
18629         this.DDM = Roo.dd.DDM;
18630         // initialize the groups array
18631         this.groups = {};
18632
18633         // assume that we have an element reference instead of an id if the
18634         // parameter is not a string
18635         if (typeof id !== "string") {
18636             id = Roo.id(id);
18637         }
18638
18639         // set the id
18640         this.id = id;
18641
18642         // add to an interaction group
18643         this.addToGroup((sGroup) ? sGroup : "default");
18644
18645         // We don't want to register this as the handle with the manager
18646         // so we just set the id rather than calling the setter.
18647         this.handleElId = id;
18648
18649         // the linked element is the element that gets dragged by default
18650         this.setDragElId(id);
18651
18652         // by default, clicked anchors will not start drag operations.
18653         this.invalidHandleTypes = { A: "A" };
18654         this.invalidHandleIds = {};
18655         this.invalidHandleClasses = [];
18656
18657         this.applyConfig();
18658
18659         this.handleOnAvailable();
18660     },
18661
18662     /**
18663      * Applies the configuration parameters that were passed into the constructor.
18664      * This is supposed to happen at each level through the inheritance chain.  So
18665      * a DDProxy implentation will execute apply config on DDProxy, DD, and
18666      * DragDrop in order to get all of the parameters that are available in
18667      * each object.
18668      * @method applyConfig
18669      */
18670     applyConfig: function() {
18671
18672         // configurable properties:
18673         //    padding, isTarget, maintainOffset, primaryButtonOnly
18674         this.padding           = this.config.padding || [0, 0, 0, 0];
18675         this.isTarget          = (this.config.isTarget !== false);
18676         this.maintainOffset    = (this.config.maintainOffset);
18677         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
18678
18679     },
18680
18681     /**
18682      * Executed when the linked element is available
18683      * @method handleOnAvailable
18684      * @private
18685      */
18686     handleOnAvailable: function() {
18687         this.available = true;
18688         this.resetConstraints();
18689         this.onAvailable();
18690     },
18691
18692      /**
18693      * Configures the padding for the target zone in px.  Effectively expands
18694      * (or reduces) the virtual object size for targeting calculations.
18695      * Supports css-style shorthand; if only one parameter is passed, all sides
18696      * will have that padding, and if only two are passed, the top and bottom
18697      * will have the first param, the left and right the second.
18698      * @method setPadding
18699      * @param {int} iTop    Top pad
18700      * @param {int} iRight  Right pad
18701      * @param {int} iBot    Bot pad
18702      * @param {int} iLeft   Left pad
18703      */
18704     setPadding: function(iTop, iRight, iBot, iLeft) {
18705         // this.padding = [iLeft, iRight, iTop, iBot];
18706         if (!iRight && 0 !== iRight) {
18707             this.padding = [iTop, iTop, iTop, iTop];
18708         } else if (!iBot && 0 !== iBot) {
18709             this.padding = [iTop, iRight, iTop, iRight];
18710         } else {
18711             this.padding = [iTop, iRight, iBot, iLeft];
18712         }
18713     },
18714
18715     /**
18716      * Stores the initial placement of the linked element.
18717      * @method setInitialPosition
18718      * @param {int} diffX   the X offset, default 0
18719      * @param {int} diffY   the Y offset, default 0
18720      */
18721     setInitPosition: function(diffX, diffY) {
18722         var el = this.getEl();
18723
18724         if (!this.DDM.verifyEl(el)) {
18725             return;
18726         }
18727
18728         var dx = diffX || 0;
18729         var dy = diffY || 0;
18730
18731         var p = Dom.getXY( el );
18732
18733         this.initPageX = p[0] - dx;
18734         this.initPageY = p[1] - dy;
18735
18736         this.lastPageX = p[0];
18737         this.lastPageY = p[1];
18738
18739
18740         this.setStartPosition(p);
18741     },
18742
18743     /**
18744      * Sets the start position of the element.  This is set when the obj
18745      * is initialized, the reset when a drag is started.
18746      * @method setStartPosition
18747      * @param pos current position (from previous lookup)
18748      * @private
18749      */
18750     setStartPosition: function(pos) {
18751         var p = pos || Dom.getXY( this.getEl() );
18752         this.deltaSetXY = null;
18753
18754         this.startPageX = p[0];
18755         this.startPageY = p[1];
18756     },
18757
18758     /**
18759      * Add this instance to a group of related drag/drop objects.  All
18760      * instances belong to at least one group, and can belong to as many
18761      * groups as needed.
18762      * @method addToGroup
18763      * @param sGroup {string} the name of the group
18764      */
18765     addToGroup: function(sGroup) {
18766         this.groups[sGroup] = true;
18767         this.DDM.regDragDrop(this, sGroup);
18768     },
18769
18770     /**
18771      * Remove's this instance from the supplied interaction group
18772      * @method removeFromGroup
18773      * @param {string}  sGroup  The group to drop
18774      */
18775     removeFromGroup: function(sGroup) {
18776         if (this.groups[sGroup]) {
18777             delete this.groups[sGroup];
18778         }
18779
18780         this.DDM.removeDDFromGroup(this, sGroup);
18781     },
18782
18783     /**
18784      * Allows you to specify that an element other than the linked element
18785      * will be moved with the cursor during a drag
18786      * @method setDragElId
18787      * @param id {string} the id of the element that will be used to initiate the drag
18788      */
18789     setDragElId: function(id) {
18790         this.dragElId = id;
18791     },
18792
18793     /**
18794      * Allows you to specify a child of the linked element that should be
18795      * used to initiate the drag operation.  An example of this would be if
18796      * you have a content div with text and links.  Clicking anywhere in the
18797      * content area would normally start the drag operation.  Use this method
18798      * to specify that an element inside of the content div is the element
18799      * that starts the drag operation.
18800      * @method setHandleElId
18801      * @param id {string} the id of the element that will be used to
18802      * initiate the drag.
18803      */
18804     setHandleElId: function(id) {
18805         if (typeof id !== "string") {
18806             id = Roo.id(id);
18807         }
18808         this.handleElId = id;
18809         this.DDM.regHandle(this.id, id);
18810     },
18811
18812     /**
18813      * Allows you to set an element outside of the linked element as a drag
18814      * handle
18815      * @method setOuterHandleElId
18816      * @param id the id of the element that will be used to initiate the drag
18817      */
18818     setOuterHandleElId: function(id) {
18819         if (typeof id !== "string") {
18820             id = Roo.id(id);
18821         }
18822         Event.on(id, "mousedown",
18823                 this.handleMouseDown, this);
18824         this.setHandleElId(id);
18825
18826         this.hasOuterHandles = true;
18827     },
18828
18829     /**
18830      * Remove all drag and drop hooks for this element
18831      * @method unreg
18832      */
18833     unreg: function() {
18834         Event.un(this.id, "mousedown",
18835                 this.handleMouseDown);
18836         Event.un(this.id, "touchstart",
18837                 this.handleMouseDown);
18838         this._domRef = null;
18839         this.DDM._remove(this);
18840     },
18841
18842     destroy : function(){
18843         this.unreg();
18844     },
18845
18846     /**
18847      * Returns true if this instance is locked, or the drag drop mgr is locked
18848      * (meaning that all drag/drop is disabled on the page.)
18849      * @method isLocked
18850      * @return {boolean} true if this obj or all drag/drop is locked, else
18851      * false
18852      */
18853     isLocked: function() {
18854         return (this.DDM.isLocked() || this.locked);
18855     },
18856
18857     /**
18858      * Fired when this object is clicked
18859      * @method handleMouseDown
18860      * @param {Event} e
18861      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
18862      * @private
18863      */
18864     handleMouseDown: function(e, oDD){
18865      
18866         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
18867             //Roo.log('not touch/ button !=0');
18868             return;
18869         }
18870         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
18871             return; // double touch..
18872         }
18873         
18874
18875         if (this.isLocked()) {
18876             //Roo.log('locked');
18877             return;
18878         }
18879
18880         this.DDM.refreshCache(this.groups);
18881 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
18882         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
18883         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
18884             //Roo.log('no outer handes or not over target');
18885                 // do nothing.
18886         } else {
18887 //            Roo.log('check validator');
18888             if (this.clickValidator(e)) {
18889 //                Roo.log('validate success');
18890                 // set the initial element position
18891                 this.setStartPosition();
18892
18893
18894                 this.b4MouseDown(e);
18895                 this.onMouseDown(e);
18896
18897                 this.DDM.handleMouseDown(e, this);
18898
18899                 this.DDM.stopEvent(e);
18900             } else {
18901
18902
18903             }
18904         }
18905     },
18906
18907     clickValidator: function(e) {
18908         var target = e.getTarget();
18909         return ( this.isValidHandleChild(target) &&
18910                     (this.id == this.handleElId ||
18911                         this.DDM.handleWasClicked(target, this.id)) );
18912     },
18913
18914     /**
18915      * Allows you to specify a tag name that should not start a drag operation
18916      * when clicked.  This is designed to facilitate embedding links within a
18917      * drag handle that do something other than start the drag.
18918      * @method addInvalidHandleType
18919      * @param {string} tagName the type of element to exclude
18920      */
18921     addInvalidHandleType: function(tagName) {
18922         var type = tagName.toUpperCase();
18923         this.invalidHandleTypes[type] = type;
18924     },
18925
18926     /**
18927      * Lets you to specify an element id for a child of a drag handle
18928      * that should not initiate a drag
18929      * @method addInvalidHandleId
18930      * @param {string} id the element id of the element you wish to ignore
18931      */
18932     addInvalidHandleId: function(id) {
18933         if (typeof id !== "string") {
18934             id = Roo.id(id);
18935         }
18936         this.invalidHandleIds[id] = id;
18937     },
18938
18939     /**
18940      * Lets you specify a css class of elements that will not initiate a drag
18941      * @method addInvalidHandleClass
18942      * @param {string} cssClass the class of the elements you wish to ignore
18943      */
18944     addInvalidHandleClass: function(cssClass) {
18945         this.invalidHandleClasses.push(cssClass);
18946     },
18947
18948     /**
18949      * Unsets an excluded tag name set by addInvalidHandleType
18950      * @method removeInvalidHandleType
18951      * @param {string} tagName the type of element to unexclude
18952      */
18953     removeInvalidHandleType: function(tagName) {
18954         var type = tagName.toUpperCase();
18955         // this.invalidHandleTypes[type] = null;
18956         delete this.invalidHandleTypes[type];
18957     },
18958
18959     /**
18960      * Unsets an invalid handle id
18961      * @method removeInvalidHandleId
18962      * @param {string} id the id of the element to re-enable
18963      */
18964     removeInvalidHandleId: function(id) {
18965         if (typeof id !== "string") {
18966             id = Roo.id(id);
18967         }
18968         delete this.invalidHandleIds[id];
18969     },
18970
18971     /**
18972      * Unsets an invalid css class
18973      * @method removeInvalidHandleClass
18974      * @param {string} cssClass the class of the element(s) you wish to
18975      * re-enable
18976      */
18977     removeInvalidHandleClass: function(cssClass) {
18978         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
18979             if (this.invalidHandleClasses[i] == cssClass) {
18980                 delete this.invalidHandleClasses[i];
18981             }
18982         }
18983     },
18984
18985     /**
18986      * Checks the tag exclusion list to see if this click should be ignored
18987      * @method isValidHandleChild
18988      * @param {HTMLElement} node the HTMLElement to evaluate
18989      * @return {boolean} true if this is a valid tag type, false if not
18990      */
18991     isValidHandleChild: function(node) {
18992
18993         var valid = true;
18994         // var n = (node.nodeName == "#text") ? node.parentNode : node;
18995         var nodeName;
18996         try {
18997             nodeName = node.nodeName.toUpperCase();
18998         } catch(e) {
18999             nodeName = node.nodeName;
19000         }
19001         valid = valid && !this.invalidHandleTypes[nodeName];
19002         valid = valid && !this.invalidHandleIds[node.id];
19003
19004         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
19005             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
19006         }
19007
19008
19009         return valid;
19010
19011     },
19012
19013     /**
19014      * Create the array of horizontal tick marks if an interval was specified
19015      * in setXConstraint().
19016      * @method setXTicks
19017      * @private
19018      */
19019     setXTicks: function(iStartX, iTickSize) {
19020         this.xTicks = [];
19021         this.xTickSize = iTickSize;
19022
19023         var tickMap = {};
19024
19025         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
19026             if (!tickMap[i]) {
19027                 this.xTicks[this.xTicks.length] = i;
19028                 tickMap[i] = true;
19029             }
19030         }
19031
19032         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
19033             if (!tickMap[i]) {
19034                 this.xTicks[this.xTicks.length] = i;
19035                 tickMap[i] = true;
19036             }
19037         }
19038
19039         this.xTicks.sort(this.DDM.numericSort) ;
19040     },
19041
19042     /**
19043      * Create the array of vertical tick marks if an interval was specified in
19044      * setYConstraint().
19045      * @method setYTicks
19046      * @private
19047      */
19048     setYTicks: function(iStartY, iTickSize) {
19049         this.yTicks = [];
19050         this.yTickSize = iTickSize;
19051
19052         var tickMap = {};
19053
19054         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
19055             if (!tickMap[i]) {
19056                 this.yTicks[this.yTicks.length] = i;
19057                 tickMap[i] = true;
19058             }
19059         }
19060
19061         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
19062             if (!tickMap[i]) {
19063                 this.yTicks[this.yTicks.length] = i;
19064                 tickMap[i] = true;
19065             }
19066         }
19067
19068         this.yTicks.sort(this.DDM.numericSort) ;
19069     },
19070
19071     /**
19072      * By default, the element can be dragged any place on the screen.  Use
19073      * this method to limit the horizontal travel of the element.  Pass in
19074      * 0,0 for the parameters if you want to lock the drag to the y axis.
19075      * @method setXConstraint
19076      * @param {int} iLeft the number of pixels the element can move to the left
19077      * @param {int} iRight the number of pixels the element can move to the
19078      * right
19079      * @param {int} iTickSize optional parameter for specifying that the
19080      * element
19081      * should move iTickSize pixels at a time.
19082      */
19083     setXConstraint: function(iLeft, iRight, iTickSize) {
19084         this.leftConstraint = iLeft;
19085         this.rightConstraint = iRight;
19086
19087         this.minX = this.initPageX - iLeft;
19088         this.maxX = this.initPageX + iRight;
19089         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19090
19091         this.constrainX = true;
19092     },
19093
19094     /**
19095      * Clears any constraints applied to this instance.  Also clears ticks
19096      * since they can't exist independent of a constraint at this time.
19097      * @method clearConstraints
19098      */
19099     clearConstraints: function() {
19100         this.constrainX = false;
19101         this.constrainY = false;
19102         this.clearTicks();
19103     },
19104
19105     /**
19106      * Clears any tick interval defined for this instance
19107      * @method clearTicks
19108      */
19109     clearTicks: function() {
19110         this.xTicks = null;
19111         this.yTicks = null;
19112         this.xTickSize = 0;
19113         this.yTickSize = 0;
19114     },
19115
19116     /**
19117      * By default, the element can be dragged any place on the screen.  Set
19118      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19119      * parameters if you want to lock the drag to the x axis.
19120      * @method setYConstraint
19121      * @param {int} iUp the number of pixels the element can move up
19122      * @param {int} iDown the number of pixels the element can move down
19123      * @param {int} iTickSize optional parameter for specifying that the
19124      * element should move iTickSize pixels at a time.
19125      */
19126     setYConstraint: function(iUp, iDown, iTickSize) {
19127         this.topConstraint = iUp;
19128         this.bottomConstraint = iDown;
19129
19130         this.minY = this.initPageY - iUp;
19131         this.maxY = this.initPageY + iDown;
19132         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19133
19134         this.constrainY = true;
19135
19136     },
19137
19138     /**
19139      * resetConstraints must be called if you manually reposition a dd element.
19140      * @method resetConstraints
19141      * @param {boolean} maintainOffset
19142      */
19143     resetConstraints: function() {
19144
19145
19146         // Maintain offsets if necessary
19147         if (this.initPageX || this.initPageX === 0) {
19148             // figure out how much this thing has moved
19149             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19150             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19151
19152             this.setInitPosition(dx, dy);
19153
19154         // This is the first time we have detected the element's position
19155         } else {
19156             this.setInitPosition();
19157         }
19158
19159         if (this.constrainX) {
19160             this.setXConstraint( this.leftConstraint,
19161                                  this.rightConstraint,
19162                                  this.xTickSize        );
19163         }
19164
19165         if (this.constrainY) {
19166             this.setYConstraint( this.topConstraint,
19167                                  this.bottomConstraint,
19168                                  this.yTickSize         );
19169         }
19170     },
19171
19172     /**
19173      * Normally the drag element is moved pixel by pixel, but we can specify
19174      * that it move a number of pixels at a time.  This method resolves the
19175      * location when we have it set up like this.
19176      * @method getTick
19177      * @param {int} val where we want to place the object
19178      * @param {int[]} tickArray sorted array of valid points
19179      * @return {int} the closest tick
19180      * @private
19181      */
19182     getTick: function(val, tickArray) {
19183
19184         if (!tickArray) {
19185             // If tick interval is not defined, it is effectively 1 pixel,
19186             // so we return the value passed to us.
19187             return val;
19188         } else if (tickArray[0] >= val) {
19189             // The value is lower than the first tick, so we return the first
19190             // tick.
19191             return tickArray[0];
19192         } else {
19193             for (var i=0, len=tickArray.length; i<len; ++i) {
19194                 var next = i + 1;
19195                 if (tickArray[next] && tickArray[next] >= val) {
19196                     var diff1 = val - tickArray[i];
19197                     var diff2 = tickArray[next] - val;
19198                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19199                 }
19200             }
19201
19202             // The value is larger than the last tick, so we return the last
19203             // tick.
19204             return tickArray[tickArray.length - 1];
19205         }
19206     },
19207
19208     /**
19209      * toString method
19210      * @method toString
19211      * @return {string} string representation of the dd obj
19212      */
19213     toString: function() {
19214         return ("DragDrop " + this.id);
19215     }
19216
19217 });
19218
19219 })();
19220 /*
19221  * Based on:
19222  * Ext JS Library 1.1.1
19223  * Copyright(c) 2006-2007, Ext JS, LLC.
19224  *
19225  * Originally Released Under LGPL - original licence link has changed is not relivant.
19226  *
19227  * Fork - LGPL
19228  * <script type="text/javascript">
19229  */
19230
19231
19232 /**
19233  * The drag and drop utility provides a framework for building drag and drop
19234  * applications.  In addition to enabling drag and drop for specific elements,
19235  * the drag and drop elements are tracked by the manager class, and the
19236  * interactions between the various elements are tracked during the drag and
19237  * the implementing code is notified about these important moments.
19238  */
19239
19240 // Only load the library once.  Rewriting the manager class would orphan
19241 // existing drag and drop instances.
19242 if (!Roo.dd.DragDropMgr) {
19243
19244 /**
19245  * @class Roo.dd.DragDropMgr
19246  * DragDropMgr is a singleton that tracks the element interaction for
19247  * all DragDrop items in the window.  Generally, you will not call
19248  * this class directly, but it does have helper methods that could
19249  * be useful in your DragDrop implementations.
19250  * @singleton
19251  */
19252 Roo.dd.DragDropMgr = function() {
19253
19254     var Event = Roo.EventManager;
19255
19256     return {
19257
19258         /**
19259          * Two dimensional Array of registered DragDrop objects.  The first
19260          * dimension is the DragDrop item group, the second the DragDrop
19261          * object.
19262          * @property ids
19263          * @type {string: string}
19264          * @private
19265          * @static
19266          */
19267         ids: {},
19268
19269         /**
19270          * Array of element ids defined as drag handles.  Used to determine
19271          * if the element that generated the mousedown event is actually the
19272          * handle and not the html element itself.
19273          * @property handleIds
19274          * @type {string: string}
19275          * @private
19276          * @static
19277          */
19278         handleIds: {},
19279
19280         /**
19281          * the DragDrop object that is currently being dragged
19282          * @property dragCurrent
19283          * @type DragDrop
19284          * @private
19285          * @static
19286          **/
19287         dragCurrent: null,
19288
19289         /**
19290          * the DragDrop object(s) that are being hovered over
19291          * @property dragOvers
19292          * @type Array
19293          * @private
19294          * @static
19295          */
19296         dragOvers: {},
19297
19298         /**
19299          * the X distance between the cursor and the object being dragged
19300          * @property deltaX
19301          * @type int
19302          * @private
19303          * @static
19304          */
19305         deltaX: 0,
19306
19307         /**
19308          * the Y distance between the cursor and the object being dragged
19309          * @property deltaY
19310          * @type int
19311          * @private
19312          * @static
19313          */
19314         deltaY: 0,
19315
19316         /**
19317          * Flag to determine if we should prevent the default behavior of the
19318          * events we define. By default this is true, but this can be set to
19319          * false if you need the default behavior (not recommended)
19320          * @property preventDefault
19321          * @type boolean
19322          * @static
19323          */
19324         preventDefault: true,
19325
19326         /**
19327          * Flag to determine if we should stop the propagation of the events
19328          * we generate. This is true by default but you may want to set it to
19329          * false if the html element contains other features that require the
19330          * mouse click.
19331          * @property stopPropagation
19332          * @type boolean
19333          * @static
19334          */
19335         stopPropagation: true,
19336
19337         /**
19338          * Internal flag that is set to true when drag and drop has been
19339          * intialized
19340          * @property initialized
19341          * @private
19342          * @static
19343          */
19344         initalized: false,
19345
19346         /**
19347          * All drag and drop can be disabled.
19348          * @property locked
19349          * @private
19350          * @static
19351          */
19352         locked: false,
19353
19354         /**
19355          * Called the first time an element is registered.
19356          * @method init
19357          * @private
19358          * @static
19359          */
19360         init: function() {
19361             this.initialized = true;
19362         },
19363
19364         /**
19365          * In point mode, drag and drop interaction is defined by the
19366          * location of the cursor during the drag/drop
19367          * @property POINT
19368          * @type int
19369          * @static
19370          */
19371         POINT: 0,
19372
19373         /**
19374          * In intersect mode, drag and drop interactio nis defined by the
19375          * overlap of two or more drag and drop objects.
19376          * @property INTERSECT
19377          * @type int
19378          * @static
19379          */
19380         INTERSECT: 1,
19381
19382         /**
19383          * The current drag and drop mode.  Default: POINT
19384          * @property mode
19385          * @type int
19386          * @static
19387          */
19388         mode: 0,
19389
19390         /**
19391          * Runs method on all drag and drop objects
19392          * @method _execOnAll
19393          * @private
19394          * @static
19395          */
19396         _execOnAll: function(sMethod, args) {
19397             for (var i in this.ids) {
19398                 for (var j in this.ids[i]) {
19399                     var oDD = this.ids[i][j];
19400                     if (! this.isTypeOfDD(oDD)) {
19401                         continue;
19402                     }
19403                     oDD[sMethod].apply(oDD, args);
19404                 }
19405             }
19406         },
19407
19408         /**
19409          * Drag and drop initialization.  Sets up the global event handlers
19410          * @method _onLoad
19411          * @private
19412          * @static
19413          */
19414         _onLoad: function() {
19415
19416             this.init();
19417
19418             if (!Roo.isTouch) {
19419                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19420                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19421             }
19422             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19423             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19424             
19425             Event.on(window,   "unload",    this._onUnload, this, true);
19426             Event.on(window,   "resize",    this._onResize, this, true);
19427             // Event.on(window,   "mouseout",    this._test);
19428
19429         },
19430
19431         /**
19432          * Reset constraints on all drag and drop objs
19433          * @method _onResize
19434          * @private
19435          * @static
19436          */
19437         _onResize: function(e) {
19438             this._execOnAll("resetConstraints", []);
19439         },
19440
19441         /**
19442          * Lock all drag and drop functionality
19443          * @method lock
19444          * @static
19445          */
19446         lock: function() { this.locked = true; },
19447
19448         /**
19449          * Unlock all drag and drop functionality
19450          * @method unlock
19451          * @static
19452          */
19453         unlock: function() { this.locked = false; },
19454
19455         /**
19456          * Is drag and drop locked?
19457          * @method isLocked
19458          * @return {boolean} True if drag and drop is locked, false otherwise.
19459          * @static
19460          */
19461         isLocked: function() { return this.locked; },
19462
19463         /**
19464          * Location cache that is set for all drag drop objects when a drag is
19465          * initiated, cleared when the drag is finished.
19466          * @property locationCache
19467          * @private
19468          * @static
19469          */
19470         locationCache: {},
19471
19472         /**
19473          * Set useCache to false if you want to force object the lookup of each
19474          * drag and drop linked element constantly during a drag.
19475          * @property useCache
19476          * @type boolean
19477          * @static
19478          */
19479         useCache: true,
19480
19481         /**
19482          * The number of pixels that the mouse needs to move after the
19483          * mousedown before the drag is initiated.  Default=3;
19484          * @property clickPixelThresh
19485          * @type int
19486          * @static
19487          */
19488         clickPixelThresh: 3,
19489
19490         /**
19491          * The number of milliseconds after the mousedown event to initiate the
19492          * drag if we don't get a mouseup event. Default=1000
19493          * @property clickTimeThresh
19494          * @type int
19495          * @static
19496          */
19497         clickTimeThresh: 350,
19498
19499         /**
19500          * Flag that indicates that either the drag pixel threshold or the
19501          * mousdown time threshold has been met
19502          * @property dragThreshMet
19503          * @type boolean
19504          * @private
19505          * @static
19506          */
19507         dragThreshMet: false,
19508
19509         /**
19510          * Timeout used for the click time threshold
19511          * @property clickTimeout
19512          * @type Object
19513          * @private
19514          * @static
19515          */
19516         clickTimeout: null,
19517
19518         /**
19519          * The X position of the mousedown event stored for later use when a
19520          * drag threshold is met.
19521          * @property startX
19522          * @type int
19523          * @private
19524          * @static
19525          */
19526         startX: 0,
19527
19528         /**
19529          * The Y position of the mousedown event stored for later use when a
19530          * drag threshold is met.
19531          * @property startY
19532          * @type int
19533          * @private
19534          * @static
19535          */
19536         startY: 0,
19537
19538         /**
19539          * Each DragDrop instance must be registered with the DragDropMgr.
19540          * This is executed in DragDrop.init()
19541          * @method regDragDrop
19542          * @param {DragDrop} oDD the DragDrop object to register
19543          * @param {String} sGroup the name of the group this element belongs to
19544          * @static
19545          */
19546         regDragDrop: function(oDD, sGroup) {
19547             if (!this.initialized) { this.init(); }
19548
19549             if (!this.ids[sGroup]) {
19550                 this.ids[sGroup] = {};
19551             }
19552             this.ids[sGroup][oDD.id] = oDD;
19553         },
19554
19555         /**
19556          * Removes the supplied dd instance from the supplied group. Executed
19557          * by DragDrop.removeFromGroup, so don't call this function directly.
19558          * @method removeDDFromGroup
19559          * @private
19560          * @static
19561          */
19562         removeDDFromGroup: function(oDD, sGroup) {
19563             if (!this.ids[sGroup]) {
19564                 this.ids[sGroup] = {};
19565             }
19566
19567             var obj = this.ids[sGroup];
19568             if (obj && obj[oDD.id]) {
19569                 delete obj[oDD.id];
19570             }
19571         },
19572
19573         /**
19574          * Unregisters a drag and drop item.  This is executed in
19575          * DragDrop.unreg, use that method instead of calling this directly.
19576          * @method _remove
19577          * @private
19578          * @static
19579          */
19580         _remove: function(oDD) {
19581             for (var g in oDD.groups) {
19582                 if (g && this.ids[g][oDD.id]) {
19583                     delete this.ids[g][oDD.id];
19584                 }
19585             }
19586             delete this.handleIds[oDD.id];
19587         },
19588
19589         /**
19590          * Each DragDrop handle element must be registered.  This is done
19591          * automatically when executing DragDrop.setHandleElId()
19592          * @method regHandle
19593          * @param {String} sDDId the DragDrop id this element is a handle for
19594          * @param {String} sHandleId the id of the element that is the drag
19595          * handle
19596          * @static
19597          */
19598         regHandle: function(sDDId, sHandleId) {
19599             if (!this.handleIds[sDDId]) {
19600                 this.handleIds[sDDId] = {};
19601             }
19602             this.handleIds[sDDId][sHandleId] = sHandleId;
19603         },
19604
19605         /**
19606          * Utility function to determine if a given element has been
19607          * registered as a drag drop item.
19608          * @method isDragDrop
19609          * @param {String} id the element id to check
19610          * @return {boolean} true if this element is a DragDrop item,
19611          * false otherwise
19612          * @static
19613          */
19614         isDragDrop: function(id) {
19615             return ( this.getDDById(id) ) ? true : false;
19616         },
19617
19618         /**
19619          * Returns the drag and drop instances that are in all groups the
19620          * passed in instance belongs to.
19621          * @method getRelated
19622          * @param {DragDrop} p_oDD the obj to get related data for
19623          * @param {boolean} bTargetsOnly if true, only return targetable objs
19624          * @return {DragDrop[]} the related instances
19625          * @static
19626          */
19627         getRelated: function(p_oDD, bTargetsOnly) {
19628             var oDDs = [];
19629             for (var i in p_oDD.groups) {
19630                 for (j in this.ids[i]) {
19631                     var dd = this.ids[i][j];
19632                     if (! this.isTypeOfDD(dd)) {
19633                         continue;
19634                     }
19635                     if (!bTargetsOnly || dd.isTarget) {
19636                         oDDs[oDDs.length] = dd;
19637                     }
19638                 }
19639             }
19640
19641             return oDDs;
19642         },
19643
19644         /**
19645          * Returns true if the specified dd target is a legal target for
19646          * the specifice drag obj
19647          * @method isLegalTarget
19648          * @param {DragDrop} the drag obj
19649          * @param {DragDrop} the target
19650          * @return {boolean} true if the target is a legal target for the
19651          * dd obj
19652          * @static
19653          */
19654         isLegalTarget: function (oDD, oTargetDD) {
19655             var targets = this.getRelated(oDD, true);
19656             for (var i=0, len=targets.length;i<len;++i) {
19657                 if (targets[i].id == oTargetDD.id) {
19658                     return true;
19659                 }
19660             }
19661
19662             return false;
19663         },
19664
19665         /**
19666          * My goal is to be able to transparently determine if an object is
19667          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
19668          * returns "object", oDD.constructor.toString() always returns
19669          * "DragDrop" and not the name of the subclass.  So for now it just
19670          * evaluates a well-known variable in DragDrop.
19671          * @method isTypeOfDD
19672          * @param {Object} the object to evaluate
19673          * @return {boolean} true if typeof oDD = DragDrop
19674          * @static
19675          */
19676         isTypeOfDD: function (oDD) {
19677             return (oDD && oDD.__ygDragDrop);
19678         },
19679
19680         /**
19681          * Utility function to determine if a given element has been
19682          * registered as a drag drop handle for the given Drag Drop object.
19683          * @method isHandle
19684          * @param {String} id the element id to check
19685          * @return {boolean} true if this element is a DragDrop handle, false
19686          * otherwise
19687          * @static
19688          */
19689         isHandle: function(sDDId, sHandleId) {
19690             return ( this.handleIds[sDDId] &&
19691                             this.handleIds[sDDId][sHandleId] );
19692         },
19693
19694         /**
19695          * Returns the DragDrop instance for a given id
19696          * @method getDDById
19697          * @param {String} id the id of the DragDrop object
19698          * @return {DragDrop} the drag drop object, null if it is not found
19699          * @static
19700          */
19701         getDDById: function(id) {
19702             for (var i in this.ids) {
19703                 if (this.ids[i][id]) {
19704                     return this.ids[i][id];
19705                 }
19706             }
19707             return null;
19708         },
19709
19710         /**
19711          * Fired after a registered DragDrop object gets the mousedown event.
19712          * Sets up the events required to track the object being dragged
19713          * @method handleMouseDown
19714          * @param {Event} e the event
19715          * @param oDD the DragDrop object being dragged
19716          * @private
19717          * @static
19718          */
19719         handleMouseDown: function(e, oDD) {
19720             if(Roo.QuickTips){
19721                 Roo.QuickTips.disable();
19722             }
19723             this.currentTarget = e.getTarget();
19724
19725             this.dragCurrent = oDD;
19726
19727             var el = oDD.getEl();
19728
19729             // track start position
19730             this.startX = e.getPageX();
19731             this.startY = e.getPageY();
19732
19733             this.deltaX = this.startX - el.offsetLeft;
19734             this.deltaY = this.startY - el.offsetTop;
19735
19736             this.dragThreshMet = false;
19737
19738             this.clickTimeout = setTimeout(
19739                     function() {
19740                         var DDM = Roo.dd.DDM;
19741                         DDM.startDrag(DDM.startX, DDM.startY);
19742                     },
19743                     this.clickTimeThresh );
19744         },
19745
19746         /**
19747          * Fired when either the drag pixel threshol or the mousedown hold
19748          * time threshold has been met.
19749          * @method startDrag
19750          * @param x {int} the X position of the original mousedown
19751          * @param y {int} the Y position of the original mousedown
19752          * @static
19753          */
19754         startDrag: function(x, y) {
19755             clearTimeout(this.clickTimeout);
19756             if (this.dragCurrent) {
19757                 this.dragCurrent.b4StartDrag(x, y);
19758                 this.dragCurrent.startDrag(x, y);
19759             }
19760             this.dragThreshMet = true;
19761         },
19762
19763         /**
19764          * Internal function to handle the mouseup event.  Will be invoked
19765          * from the context of the document.
19766          * @method handleMouseUp
19767          * @param {Event} e the event
19768          * @private
19769          * @static
19770          */
19771         handleMouseUp: function(e) {
19772
19773             if(Roo.QuickTips){
19774                 Roo.QuickTips.enable();
19775             }
19776             if (! this.dragCurrent) {
19777                 return;
19778             }
19779
19780             clearTimeout(this.clickTimeout);
19781
19782             if (this.dragThreshMet) {
19783                 this.fireEvents(e, true);
19784             } else {
19785             }
19786
19787             this.stopDrag(e);
19788
19789             this.stopEvent(e);
19790         },
19791
19792         /**
19793          * Utility to stop event propagation and event default, if these
19794          * features are turned on.
19795          * @method stopEvent
19796          * @param {Event} e the event as returned by this.getEvent()
19797          * @static
19798          */
19799         stopEvent: function(e){
19800             if(this.stopPropagation) {
19801                 e.stopPropagation();
19802             }
19803
19804             if (this.preventDefault) {
19805                 e.preventDefault();
19806             }
19807         },
19808
19809         /**
19810          * Internal function to clean up event handlers after the drag
19811          * operation is complete
19812          * @method stopDrag
19813          * @param {Event} e the event
19814          * @private
19815          * @static
19816          */
19817         stopDrag: function(e) {
19818             // Fire the drag end event for the item that was dragged
19819             if (this.dragCurrent) {
19820                 if (this.dragThreshMet) {
19821                     this.dragCurrent.b4EndDrag(e);
19822                     this.dragCurrent.endDrag(e);
19823                 }
19824
19825                 this.dragCurrent.onMouseUp(e);
19826             }
19827
19828             this.dragCurrent = null;
19829             this.dragOvers = {};
19830         },
19831
19832         /**
19833          * Internal function to handle the mousemove event.  Will be invoked
19834          * from the context of the html element.
19835          *
19836          * @TODO figure out what we can do about mouse events lost when the
19837          * user drags objects beyond the window boundary.  Currently we can
19838          * detect this in internet explorer by verifying that the mouse is
19839          * down during the mousemove event.  Firefox doesn't give us the
19840          * button state on the mousemove event.
19841          * @method handleMouseMove
19842          * @param {Event} e the event
19843          * @private
19844          * @static
19845          */
19846         handleMouseMove: function(e) {
19847             if (! this.dragCurrent) {
19848                 return true;
19849             }
19850
19851             // var button = e.which || e.button;
19852
19853             // check for IE mouseup outside of page boundary
19854             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
19855                 this.stopEvent(e);
19856                 return this.handleMouseUp(e);
19857             }
19858
19859             if (!this.dragThreshMet) {
19860                 var diffX = Math.abs(this.startX - e.getPageX());
19861                 var diffY = Math.abs(this.startY - e.getPageY());
19862                 if (diffX > this.clickPixelThresh ||
19863                             diffY > this.clickPixelThresh) {
19864                     this.startDrag(this.startX, this.startY);
19865                 }
19866             }
19867
19868             if (this.dragThreshMet) {
19869                 this.dragCurrent.b4Drag(e);
19870                 this.dragCurrent.onDrag(e);
19871                 if(!this.dragCurrent.moveOnly){
19872                     this.fireEvents(e, false);
19873                 }
19874             }
19875
19876             this.stopEvent(e);
19877
19878             return true;
19879         },
19880
19881         /**
19882          * Iterates over all of the DragDrop elements to find ones we are
19883          * hovering over or dropping on
19884          * @method fireEvents
19885          * @param {Event} e the event
19886          * @param {boolean} isDrop is this a drop op or a mouseover op?
19887          * @private
19888          * @static
19889          */
19890         fireEvents: function(e, isDrop) {
19891             var dc = this.dragCurrent;
19892
19893             // If the user did the mouse up outside of the window, we could
19894             // get here even though we have ended the drag.
19895             if (!dc || dc.isLocked()) {
19896                 return;
19897             }
19898
19899             var pt = e.getPoint();
19900
19901             // cache the previous dragOver array
19902             var oldOvers = [];
19903
19904             var outEvts   = [];
19905             var overEvts  = [];
19906             var dropEvts  = [];
19907             var enterEvts = [];
19908
19909             // Check to see if the object(s) we were hovering over is no longer
19910             // being hovered over so we can fire the onDragOut event
19911             for (var i in this.dragOvers) {
19912
19913                 var ddo = this.dragOvers[i];
19914
19915                 if (! this.isTypeOfDD(ddo)) {
19916                     continue;
19917                 }
19918
19919                 if (! this.isOverTarget(pt, ddo, this.mode)) {
19920                     outEvts.push( ddo );
19921                 }
19922
19923                 oldOvers[i] = true;
19924                 delete this.dragOvers[i];
19925             }
19926
19927             for (var sGroup in dc.groups) {
19928
19929                 if ("string" != typeof sGroup) {
19930                     continue;
19931                 }
19932
19933                 for (i in this.ids[sGroup]) {
19934                     var oDD = this.ids[sGroup][i];
19935                     if (! this.isTypeOfDD(oDD)) {
19936                         continue;
19937                     }
19938
19939                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
19940                         if (this.isOverTarget(pt, oDD, this.mode)) {
19941                             // look for drop interactions
19942                             if (isDrop) {
19943                                 dropEvts.push( oDD );
19944                             // look for drag enter and drag over interactions
19945                             } else {
19946
19947                                 // initial drag over: dragEnter fires
19948                                 if (!oldOvers[oDD.id]) {
19949                                     enterEvts.push( oDD );
19950                                 // subsequent drag overs: dragOver fires
19951                                 } else {
19952                                     overEvts.push( oDD );
19953                                 }
19954
19955                                 this.dragOvers[oDD.id] = oDD;
19956                             }
19957                         }
19958                     }
19959                 }
19960             }
19961
19962             if (this.mode) {
19963                 if (outEvts.length) {
19964                     dc.b4DragOut(e, outEvts);
19965                     dc.onDragOut(e, outEvts);
19966                 }
19967
19968                 if (enterEvts.length) {
19969                     dc.onDragEnter(e, enterEvts);
19970                 }
19971
19972                 if (overEvts.length) {
19973                     dc.b4DragOver(e, overEvts);
19974                     dc.onDragOver(e, overEvts);
19975                 }
19976
19977                 if (dropEvts.length) {
19978                     dc.b4DragDrop(e, dropEvts);
19979                     dc.onDragDrop(e, dropEvts);
19980                 }
19981
19982             } else {
19983                 // fire dragout events
19984                 var len = 0;
19985                 for (i=0, len=outEvts.length; i<len; ++i) {
19986                     dc.b4DragOut(e, outEvts[i].id);
19987                     dc.onDragOut(e, outEvts[i].id);
19988                 }
19989
19990                 // fire enter events
19991                 for (i=0,len=enterEvts.length; i<len; ++i) {
19992                     // dc.b4DragEnter(e, oDD.id);
19993                     dc.onDragEnter(e, enterEvts[i].id);
19994                 }
19995
19996                 // fire over events
19997                 for (i=0,len=overEvts.length; i<len; ++i) {
19998                     dc.b4DragOver(e, overEvts[i].id);
19999                     dc.onDragOver(e, overEvts[i].id);
20000                 }
20001
20002                 // fire drop events
20003                 for (i=0, len=dropEvts.length; i<len; ++i) {
20004                     dc.b4DragDrop(e, dropEvts[i].id);
20005                     dc.onDragDrop(e, dropEvts[i].id);
20006                 }
20007
20008             }
20009
20010             // notify about a drop that did not find a target
20011             if (isDrop && !dropEvts.length) {
20012                 dc.onInvalidDrop(e);
20013             }
20014
20015         },
20016
20017         /**
20018          * Helper function for getting the best match from the list of drag
20019          * and drop objects returned by the drag and drop events when we are
20020          * in INTERSECT mode.  It returns either the first object that the
20021          * cursor is over, or the object that has the greatest overlap with
20022          * the dragged element.
20023          * @method getBestMatch
20024          * @param  {DragDrop[]} dds The array of drag and drop objects
20025          * targeted
20026          * @return {DragDrop}       The best single match
20027          * @static
20028          */
20029         getBestMatch: function(dds) {
20030             var winner = null;
20031             // Return null if the input is not what we expect
20032             //if (!dds || !dds.length || dds.length == 0) {
20033                // winner = null;
20034             // If there is only one item, it wins
20035             //} else if (dds.length == 1) {
20036
20037             var len = dds.length;
20038
20039             if (len == 1) {
20040                 winner = dds[0];
20041             } else {
20042                 // Loop through the targeted items
20043                 for (var i=0; i<len; ++i) {
20044                     var dd = dds[i];
20045                     // If the cursor is over the object, it wins.  If the
20046                     // cursor is over multiple matches, the first one we come
20047                     // to wins.
20048                     if (dd.cursorIsOver) {
20049                         winner = dd;
20050                         break;
20051                     // Otherwise the object with the most overlap wins
20052                     } else {
20053                         if (!winner ||
20054                             winner.overlap.getArea() < dd.overlap.getArea()) {
20055                             winner = dd;
20056                         }
20057                     }
20058                 }
20059             }
20060
20061             return winner;
20062         },
20063
20064         /**
20065          * Refreshes the cache of the top-left and bottom-right points of the
20066          * drag and drop objects in the specified group(s).  This is in the
20067          * format that is stored in the drag and drop instance, so typical
20068          * usage is:
20069          * <code>
20070          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
20071          * </code>
20072          * Alternatively:
20073          * <code>
20074          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
20075          * </code>
20076          * @TODO this really should be an indexed array.  Alternatively this
20077          * method could accept both.
20078          * @method refreshCache
20079          * @param {Object} groups an associative array of groups to refresh
20080          * @static
20081          */
20082         refreshCache: function(groups) {
20083             for (var sGroup in groups) {
20084                 if ("string" != typeof sGroup) {
20085                     continue;
20086                 }
20087                 for (var i in this.ids[sGroup]) {
20088                     var oDD = this.ids[sGroup][i];
20089
20090                     if (this.isTypeOfDD(oDD)) {
20091                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20092                         var loc = this.getLocation(oDD);
20093                         if (loc) {
20094                             this.locationCache[oDD.id] = loc;
20095                         } else {
20096                             delete this.locationCache[oDD.id];
20097                             // this will unregister the drag and drop object if
20098                             // the element is not in a usable state
20099                             // oDD.unreg();
20100                         }
20101                     }
20102                 }
20103             }
20104         },
20105
20106         /**
20107          * This checks to make sure an element exists and is in the DOM.  The
20108          * main purpose is to handle cases where innerHTML is used to remove
20109          * drag and drop objects from the DOM.  IE provides an 'unspecified
20110          * error' when trying to access the offsetParent of such an element
20111          * @method verifyEl
20112          * @param {HTMLElement} el the element to check
20113          * @return {boolean} true if the element looks usable
20114          * @static
20115          */
20116         verifyEl: function(el) {
20117             if (el) {
20118                 var parent;
20119                 if(Roo.isIE){
20120                     try{
20121                         parent = el.offsetParent;
20122                     }catch(e){}
20123                 }else{
20124                     parent = el.offsetParent;
20125                 }
20126                 if (parent) {
20127                     return true;
20128                 }
20129             }
20130
20131             return false;
20132         },
20133
20134         /**
20135          * Returns a Region object containing the drag and drop element's position
20136          * and size, including the padding configured for it
20137          * @method getLocation
20138          * @param {DragDrop} oDD the drag and drop object to get the
20139          *                       location for
20140          * @return {Roo.lib.Region} a Region object representing the total area
20141          *                             the element occupies, including any padding
20142          *                             the instance is configured for.
20143          * @static
20144          */
20145         getLocation: function(oDD) {
20146             if (! this.isTypeOfDD(oDD)) {
20147                 return null;
20148             }
20149
20150             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20151
20152             try {
20153                 pos= Roo.lib.Dom.getXY(el);
20154             } catch (e) { }
20155
20156             if (!pos) {
20157                 return null;
20158             }
20159
20160             x1 = pos[0];
20161             x2 = x1 + el.offsetWidth;
20162             y1 = pos[1];
20163             y2 = y1 + el.offsetHeight;
20164
20165             t = y1 - oDD.padding[0];
20166             r = x2 + oDD.padding[1];
20167             b = y2 + oDD.padding[2];
20168             l = x1 - oDD.padding[3];
20169
20170             return new Roo.lib.Region( t, r, b, l );
20171         },
20172
20173         /**
20174          * Checks the cursor location to see if it over the target
20175          * @method isOverTarget
20176          * @param {Roo.lib.Point} pt The point to evaluate
20177          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20178          * @return {boolean} true if the mouse is over the target
20179          * @private
20180          * @static
20181          */
20182         isOverTarget: function(pt, oTarget, intersect) {
20183             // use cache if available
20184             var loc = this.locationCache[oTarget.id];
20185             if (!loc || !this.useCache) {
20186                 loc = this.getLocation(oTarget);
20187                 this.locationCache[oTarget.id] = loc;
20188
20189             }
20190
20191             if (!loc) {
20192                 return false;
20193             }
20194
20195             oTarget.cursorIsOver = loc.contains( pt );
20196
20197             // DragDrop is using this as a sanity check for the initial mousedown
20198             // in this case we are done.  In POINT mode, if the drag obj has no
20199             // contraints, we are also done. Otherwise we need to evaluate the
20200             // location of the target as related to the actual location of the
20201             // dragged element.
20202             var dc = this.dragCurrent;
20203             if (!dc || !dc.getTargetCoord ||
20204                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20205                 return oTarget.cursorIsOver;
20206             }
20207
20208             oTarget.overlap = null;
20209
20210             // Get the current location of the drag element, this is the
20211             // location of the mouse event less the delta that represents
20212             // where the original mousedown happened on the element.  We
20213             // need to consider constraints and ticks as well.
20214             var pos = dc.getTargetCoord(pt.x, pt.y);
20215
20216             var el = dc.getDragEl();
20217             var curRegion = new Roo.lib.Region( pos.y,
20218                                                    pos.x + el.offsetWidth,
20219                                                    pos.y + el.offsetHeight,
20220                                                    pos.x );
20221
20222             var overlap = curRegion.intersect(loc);
20223
20224             if (overlap) {
20225                 oTarget.overlap = overlap;
20226                 return (intersect) ? true : oTarget.cursorIsOver;
20227             } else {
20228                 return false;
20229             }
20230         },
20231
20232         /**
20233          * unload event handler
20234          * @method _onUnload
20235          * @private
20236          * @static
20237          */
20238         _onUnload: function(e, me) {
20239             Roo.dd.DragDropMgr.unregAll();
20240         },
20241
20242         /**
20243          * Cleans up the drag and drop events and objects.
20244          * @method unregAll
20245          * @private
20246          * @static
20247          */
20248         unregAll: function() {
20249
20250             if (this.dragCurrent) {
20251                 this.stopDrag();
20252                 this.dragCurrent = null;
20253             }
20254
20255             this._execOnAll("unreg", []);
20256
20257             for (i in this.elementCache) {
20258                 delete this.elementCache[i];
20259             }
20260
20261             this.elementCache = {};
20262             this.ids = {};
20263         },
20264
20265         /**
20266          * A cache of DOM elements
20267          * @property elementCache
20268          * @private
20269          * @static
20270          */
20271         elementCache: {},
20272
20273         /**
20274          * Get the wrapper for the DOM element specified
20275          * @method getElWrapper
20276          * @param {String} id the id of the element to get
20277          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20278          * @private
20279          * @deprecated This wrapper isn't that useful
20280          * @static
20281          */
20282         getElWrapper: function(id) {
20283             var oWrapper = this.elementCache[id];
20284             if (!oWrapper || !oWrapper.el) {
20285                 oWrapper = this.elementCache[id] =
20286                     new this.ElementWrapper(Roo.getDom(id));
20287             }
20288             return oWrapper;
20289         },
20290
20291         /**
20292          * Returns the actual DOM element
20293          * @method getElement
20294          * @param {String} id the id of the elment to get
20295          * @return {Object} The element
20296          * @deprecated use Roo.getDom instead
20297          * @static
20298          */
20299         getElement: function(id) {
20300             return Roo.getDom(id);
20301         },
20302
20303         /**
20304          * Returns the style property for the DOM element (i.e.,
20305          * document.getElById(id).style)
20306          * @method getCss
20307          * @param {String} id the id of the elment to get
20308          * @return {Object} The style property of the element
20309          * @deprecated use Roo.getDom instead
20310          * @static
20311          */
20312         getCss: function(id) {
20313             var el = Roo.getDom(id);
20314             return (el) ? el.style : null;
20315         },
20316
20317         /**
20318          * Inner class for cached elements
20319          * @class DragDropMgr.ElementWrapper
20320          * @for DragDropMgr
20321          * @private
20322          * @deprecated
20323          */
20324         ElementWrapper: function(el) {
20325                 /**
20326                  * The element
20327                  * @property el
20328                  */
20329                 this.el = el || null;
20330                 /**
20331                  * The element id
20332                  * @property id
20333                  */
20334                 this.id = this.el && el.id;
20335                 /**
20336                  * A reference to the style property
20337                  * @property css
20338                  */
20339                 this.css = this.el && el.style;
20340             },
20341
20342         /**
20343          * Returns the X position of an html element
20344          * @method getPosX
20345          * @param el the element for which to get the position
20346          * @return {int} the X coordinate
20347          * @for DragDropMgr
20348          * @deprecated use Roo.lib.Dom.getX instead
20349          * @static
20350          */
20351         getPosX: function(el) {
20352             return Roo.lib.Dom.getX(el);
20353         },
20354
20355         /**
20356          * Returns the Y position of an html element
20357          * @method getPosY
20358          * @param el the element for which to get the position
20359          * @return {int} the Y coordinate
20360          * @deprecated use Roo.lib.Dom.getY instead
20361          * @static
20362          */
20363         getPosY: function(el) {
20364             return Roo.lib.Dom.getY(el);
20365         },
20366
20367         /**
20368          * Swap two nodes.  In IE, we use the native method, for others we
20369          * emulate the IE behavior
20370          * @method swapNode
20371          * @param n1 the first node to swap
20372          * @param n2 the other node to swap
20373          * @static
20374          */
20375         swapNode: function(n1, n2) {
20376             if (n1.swapNode) {
20377                 n1.swapNode(n2);
20378             } else {
20379                 var p = n2.parentNode;
20380                 var s = n2.nextSibling;
20381
20382                 if (s == n1) {
20383                     p.insertBefore(n1, n2);
20384                 } else if (n2 == n1.nextSibling) {
20385                     p.insertBefore(n2, n1);
20386                 } else {
20387                     n1.parentNode.replaceChild(n2, n1);
20388                     p.insertBefore(n1, s);
20389                 }
20390             }
20391         },
20392
20393         /**
20394          * Returns the current scroll position
20395          * @method getScroll
20396          * @private
20397          * @static
20398          */
20399         getScroll: function () {
20400             var t, l, dde=document.documentElement, db=document.body;
20401             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20402                 t = dde.scrollTop;
20403                 l = dde.scrollLeft;
20404             } else if (db) {
20405                 t = db.scrollTop;
20406                 l = db.scrollLeft;
20407             } else {
20408
20409             }
20410             return { top: t, left: l };
20411         },
20412
20413         /**
20414          * Returns the specified element style property
20415          * @method getStyle
20416          * @param {HTMLElement} el          the element
20417          * @param {string}      styleProp   the style property
20418          * @return {string} The value of the style property
20419          * @deprecated use Roo.lib.Dom.getStyle
20420          * @static
20421          */
20422         getStyle: function(el, styleProp) {
20423             return Roo.fly(el).getStyle(styleProp);
20424         },
20425
20426         /**
20427          * Gets the scrollTop
20428          * @method getScrollTop
20429          * @return {int} the document's scrollTop
20430          * @static
20431          */
20432         getScrollTop: function () { return this.getScroll().top; },
20433
20434         /**
20435          * Gets the scrollLeft
20436          * @method getScrollLeft
20437          * @return {int} the document's scrollTop
20438          * @static
20439          */
20440         getScrollLeft: function () { return this.getScroll().left; },
20441
20442         /**
20443          * Sets the x/y position of an element to the location of the
20444          * target element.
20445          * @method moveToEl
20446          * @param {HTMLElement} moveEl      The element to move
20447          * @param {HTMLElement} targetEl    The position reference element
20448          * @static
20449          */
20450         moveToEl: function (moveEl, targetEl) {
20451             var aCoord = Roo.lib.Dom.getXY(targetEl);
20452             Roo.lib.Dom.setXY(moveEl, aCoord);
20453         },
20454
20455         /**
20456          * Numeric array sort function
20457          * @method numericSort
20458          * @static
20459          */
20460         numericSort: function(a, b) { return (a - b); },
20461
20462         /**
20463          * Internal counter
20464          * @property _timeoutCount
20465          * @private
20466          * @static
20467          */
20468         _timeoutCount: 0,
20469
20470         /**
20471          * Trying to make the load order less important.  Without this we get
20472          * an error if this file is loaded before the Event Utility.
20473          * @method _addListeners
20474          * @private
20475          * @static
20476          */
20477         _addListeners: function() {
20478             var DDM = Roo.dd.DDM;
20479             if ( Roo.lib.Event && document ) {
20480                 DDM._onLoad();
20481             } else {
20482                 if (DDM._timeoutCount > 2000) {
20483                 } else {
20484                     setTimeout(DDM._addListeners, 10);
20485                     if (document && document.body) {
20486                         DDM._timeoutCount += 1;
20487                     }
20488                 }
20489             }
20490         },
20491
20492         /**
20493          * Recursively searches the immediate parent and all child nodes for
20494          * the handle element in order to determine wheter or not it was
20495          * clicked.
20496          * @method handleWasClicked
20497          * @param node the html element to inspect
20498          * @static
20499          */
20500         handleWasClicked: function(node, id) {
20501             if (this.isHandle(id, node.id)) {
20502                 return true;
20503             } else {
20504                 // check to see if this is a text node child of the one we want
20505                 var p = node.parentNode;
20506
20507                 while (p) {
20508                     if (this.isHandle(id, p.id)) {
20509                         return true;
20510                     } else {
20511                         p = p.parentNode;
20512                     }
20513                 }
20514             }
20515
20516             return false;
20517         }
20518
20519     };
20520
20521 }();
20522
20523 // shorter alias, save a few bytes
20524 Roo.dd.DDM = Roo.dd.DragDropMgr;
20525 Roo.dd.DDM._addListeners();
20526
20527 }/*
20528  * Based on:
20529  * Ext JS Library 1.1.1
20530  * Copyright(c) 2006-2007, Ext JS, LLC.
20531  *
20532  * Originally Released Under LGPL - original licence link has changed is not relivant.
20533  *
20534  * Fork - LGPL
20535  * <script type="text/javascript">
20536  */
20537
20538 /**
20539  * @class Roo.dd.DD
20540  * A DragDrop implementation where the linked element follows the
20541  * mouse cursor during a drag.
20542  * @extends Roo.dd.DragDrop
20543  * @constructor
20544  * @param {String} id the id of the linked element
20545  * @param {String} sGroup the group of related DragDrop items
20546  * @param {object} config an object containing configurable attributes
20547  *                Valid properties for DD:
20548  *                    scroll
20549  */
20550 Roo.dd.DD = function(id, sGroup, config) {
20551     if (id) {
20552         this.init(id, sGroup, config);
20553     }
20554 };
20555
20556 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
20557
20558     /**
20559      * When set to true, the utility automatically tries to scroll the browser
20560      * window wehn a drag and drop element is dragged near the viewport boundary.
20561      * Defaults to true.
20562      * @property scroll
20563      * @type boolean
20564      */
20565     scroll: true,
20566
20567     /**
20568      * Sets the pointer offset to the distance between the linked element's top
20569      * left corner and the location the element was clicked
20570      * @method autoOffset
20571      * @param {int} iPageX the X coordinate of the click
20572      * @param {int} iPageY the Y coordinate of the click
20573      */
20574     autoOffset: function(iPageX, iPageY) {
20575         var x = iPageX - this.startPageX;
20576         var y = iPageY - this.startPageY;
20577         this.setDelta(x, y);
20578     },
20579
20580     /**
20581      * Sets the pointer offset.  You can call this directly to force the
20582      * offset to be in a particular location (e.g., pass in 0,0 to set it
20583      * to the center of the object)
20584      * @method setDelta
20585      * @param {int} iDeltaX the distance from the left
20586      * @param {int} iDeltaY the distance from the top
20587      */
20588     setDelta: function(iDeltaX, iDeltaY) {
20589         this.deltaX = iDeltaX;
20590         this.deltaY = iDeltaY;
20591     },
20592
20593     /**
20594      * Sets the drag element to the location of the mousedown or click event,
20595      * maintaining the cursor location relative to the location on the element
20596      * that was clicked.  Override this if you want to place the element in a
20597      * location other than where the cursor is.
20598      * @method setDragElPos
20599      * @param {int} iPageX the X coordinate of the mousedown or drag event
20600      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20601      */
20602     setDragElPos: function(iPageX, iPageY) {
20603         // the first time we do this, we are going to check to make sure
20604         // the element has css positioning
20605
20606         var el = this.getDragEl();
20607         this.alignElWithMouse(el, iPageX, iPageY);
20608     },
20609
20610     /**
20611      * Sets the element to the location of the mousedown or click event,
20612      * maintaining the cursor location relative to the location on the element
20613      * that was clicked.  Override this if you want to place the element in a
20614      * location other than where the cursor is.
20615      * @method alignElWithMouse
20616      * @param {HTMLElement} el the element to move
20617      * @param {int} iPageX the X coordinate of the mousedown or drag event
20618      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20619      */
20620     alignElWithMouse: function(el, iPageX, iPageY) {
20621         var oCoord = this.getTargetCoord(iPageX, iPageY);
20622         var fly = el.dom ? el : Roo.fly(el);
20623         if (!this.deltaSetXY) {
20624             var aCoord = [oCoord.x, oCoord.y];
20625             fly.setXY(aCoord);
20626             var newLeft = fly.getLeft(true);
20627             var newTop  = fly.getTop(true);
20628             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
20629         } else {
20630             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
20631         }
20632
20633         this.cachePosition(oCoord.x, oCoord.y);
20634         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
20635         return oCoord;
20636     },
20637
20638     /**
20639      * Saves the most recent position so that we can reset the constraints and
20640      * tick marks on-demand.  We need to know this so that we can calculate the
20641      * number of pixels the element is offset from its original position.
20642      * @method cachePosition
20643      * @param iPageX the current x position (optional, this just makes it so we
20644      * don't have to look it up again)
20645      * @param iPageY the current y position (optional, this just makes it so we
20646      * don't have to look it up again)
20647      */
20648     cachePosition: function(iPageX, iPageY) {
20649         if (iPageX) {
20650             this.lastPageX = iPageX;
20651             this.lastPageY = iPageY;
20652         } else {
20653             var aCoord = Roo.lib.Dom.getXY(this.getEl());
20654             this.lastPageX = aCoord[0];
20655             this.lastPageY = aCoord[1];
20656         }
20657     },
20658
20659     /**
20660      * Auto-scroll the window if the dragged object has been moved beyond the
20661      * visible window boundary.
20662      * @method autoScroll
20663      * @param {int} x the drag element's x position
20664      * @param {int} y the drag element's y position
20665      * @param {int} h the height of the drag element
20666      * @param {int} w the width of the drag element
20667      * @private
20668      */
20669     autoScroll: function(x, y, h, w) {
20670
20671         if (this.scroll) {
20672             // The client height
20673             var clientH = Roo.lib.Dom.getViewWidth();
20674
20675             // The client width
20676             var clientW = Roo.lib.Dom.getViewHeight();
20677
20678             // The amt scrolled down
20679             var st = this.DDM.getScrollTop();
20680
20681             // The amt scrolled right
20682             var sl = this.DDM.getScrollLeft();
20683
20684             // Location of the bottom of the element
20685             var bot = h + y;
20686
20687             // Location of the right of the element
20688             var right = w + x;
20689
20690             // The distance from the cursor to the bottom of the visible area,
20691             // adjusted so that we don't scroll if the cursor is beyond the
20692             // element drag constraints
20693             var toBot = (clientH + st - y - this.deltaY);
20694
20695             // The distance from the cursor to the right of the visible area
20696             var toRight = (clientW + sl - x - this.deltaX);
20697
20698
20699             // How close to the edge the cursor must be before we scroll
20700             // var thresh = (document.all) ? 100 : 40;
20701             var thresh = 40;
20702
20703             // How many pixels to scroll per autoscroll op.  This helps to reduce
20704             // clunky scrolling. IE is more sensitive about this ... it needs this
20705             // value to be higher.
20706             var scrAmt = (document.all) ? 80 : 30;
20707
20708             // Scroll down if we are near the bottom of the visible page and the
20709             // obj extends below the crease
20710             if ( bot > clientH && toBot < thresh ) {
20711                 window.scrollTo(sl, st + scrAmt);
20712             }
20713
20714             // Scroll up if the window is scrolled down and the top of the object
20715             // goes above the top border
20716             if ( y < st && st > 0 && y - st < thresh ) {
20717                 window.scrollTo(sl, st - scrAmt);
20718             }
20719
20720             // Scroll right if the obj is beyond the right border and the cursor is
20721             // near the border.
20722             if ( right > clientW && toRight < thresh ) {
20723                 window.scrollTo(sl + scrAmt, st);
20724             }
20725
20726             // Scroll left if the window has been scrolled to the right and the obj
20727             // extends past the left border
20728             if ( x < sl && sl > 0 && x - sl < thresh ) {
20729                 window.scrollTo(sl - scrAmt, st);
20730             }
20731         }
20732     },
20733
20734     /**
20735      * Finds the location the element should be placed if we want to move
20736      * it to where the mouse location less the click offset would place us.
20737      * @method getTargetCoord
20738      * @param {int} iPageX the X coordinate of the click
20739      * @param {int} iPageY the Y coordinate of the click
20740      * @return an object that contains the coordinates (Object.x and Object.y)
20741      * @private
20742      */
20743     getTargetCoord: function(iPageX, iPageY) {
20744
20745
20746         var x = iPageX - this.deltaX;
20747         var y = iPageY - this.deltaY;
20748
20749         if (this.constrainX) {
20750             if (x < this.minX) { x = this.minX; }
20751             if (x > this.maxX) { x = this.maxX; }
20752         }
20753
20754         if (this.constrainY) {
20755             if (y < this.minY) { y = this.minY; }
20756             if (y > this.maxY) { y = this.maxY; }
20757         }
20758
20759         x = this.getTick(x, this.xTicks);
20760         y = this.getTick(y, this.yTicks);
20761
20762
20763         return {x:x, y:y};
20764     },
20765
20766     /*
20767      * Sets up config options specific to this class. Overrides
20768      * Roo.dd.DragDrop, but all versions of this method through the
20769      * inheritance chain are called
20770      */
20771     applyConfig: function() {
20772         Roo.dd.DD.superclass.applyConfig.call(this);
20773         this.scroll = (this.config.scroll !== false);
20774     },
20775
20776     /*
20777      * Event that fires prior to the onMouseDown event.  Overrides
20778      * Roo.dd.DragDrop.
20779      */
20780     b4MouseDown: function(e) {
20781         // this.resetConstraints();
20782         this.autoOffset(e.getPageX(),
20783                             e.getPageY());
20784     },
20785
20786     /*
20787      * Event that fires prior to the onDrag event.  Overrides
20788      * Roo.dd.DragDrop.
20789      */
20790     b4Drag: function(e) {
20791         this.setDragElPos(e.getPageX(),
20792                             e.getPageY());
20793     },
20794
20795     toString: function() {
20796         return ("DD " + this.id);
20797     }
20798
20799     //////////////////////////////////////////////////////////////////////////
20800     // Debugging ygDragDrop events that can be overridden
20801     //////////////////////////////////////////////////////////////////////////
20802     /*
20803     startDrag: function(x, y) {
20804     },
20805
20806     onDrag: function(e) {
20807     },
20808
20809     onDragEnter: function(e, id) {
20810     },
20811
20812     onDragOver: function(e, id) {
20813     },
20814
20815     onDragOut: function(e, id) {
20816     },
20817
20818     onDragDrop: function(e, id) {
20819     },
20820
20821     endDrag: function(e) {
20822     }
20823
20824     */
20825
20826 });/*
20827  * Based on:
20828  * Ext JS Library 1.1.1
20829  * Copyright(c) 2006-2007, Ext JS, LLC.
20830  *
20831  * Originally Released Under LGPL - original licence link has changed is not relivant.
20832  *
20833  * Fork - LGPL
20834  * <script type="text/javascript">
20835  */
20836
20837 /**
20838  * @class Roo.dd.DDProxy
20839  * A DragDrop implementation that inserts an empty, bordered div into
20840  * the document that follows the cursor during drag operations.  At the time of
20841  * the click, the frame div is resized to the dimensions of the linked html
20842  * element, and moved to the exact location of the linked element.
20843  *
20844  * References to the "frame" element refer to the single proxy element that
20845  * was created to be dragged in place of all DDProxy elements on the
20846  * page.
20847  *
20848  * @extends Roo.dd.DD
20849  * @constructor
20850  * @param {String} id the id of the linked html element
20851  * @param {String} sGroup the group of related DragDrop objects
20852  * @param {object} config an object containing configurable attributes
20853  *                Valid properties for DDProxy in addition to those in DragDrop:
20854  *                   resizeFrame, centerFrame, dragElId
20855  */
20856 Roo.dd.DDProxy = function(id, sGroup, config) {
20857     if (id) {
20858         this.init(id, sGroup, config);
20859         this.initFrame();
20860     }
20861 };
20862
20863 /**
20864  * The default drag frame div id
20865  * @property Roo.dd.DDProxy.dragElId
20866  * @type String
20867  * @static
20868  */
20869 Roo.dd.DDProxy.dragElId = "ygddfdiv";
20870
20871 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
20872
20873     /**
20874      * By default we resize the drag frame to be the same size as the element
20875      * we want to drag (this is to get the frame effect).  We can turn it off
20876      * if we want a different behavior.
20877      * @property resizeFrame
20878      * @type boolean
20879      */
20880     resizeFrame: true,
20881
20882     /**
20883      * By default the frame is positioned exactly where the drag element is, so
20884      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
20885      * you do not have constraints on the obj is to have the drag frame centered
20886      * around the cursor.  Set centerFrame to true for this effect.
20887      * @property centerFrame
20888      * @type boolean
20889      */
20890     centerFrame: false,
20891
20892     /**
20893      * Creates the proxy element if it does not yet exist
20894      * @method createFrame
20895      */
20896     createFrame: function() {
20897         var self = this;
20898         var body = document.body;
20899
20900         if (!body || !body.firstChild) {
20901             setTimeout( function() { self.createFrame(); }, 50 );
20902             return;
20903         }
20904
20905         var div = this.getDragEl();
20906
20907         if (!div) {
20908             div    = document.createElement("div");
20909             div.id = this.dragElId;
20910             var s  = div.style;
20911
20912             s.position   = "absolute";
20913             s.visibility = "hidden";
20914             s.cursor     = "move";
20915             s.border     = "2px solid #aaa";
20916             s.zIndex     = 999;
20917
20918             // appendChild can blow up IE if invoked prior to the window load event
20919             // while rendering a table.  It is possible there are other scenarios
20920             // that would cause this to happen as well.
20921             body.insertBefore(div, body.firstChild);
20922         }
20923     },
20924
20925     /**
20926      * Initialization for the drag frame element.  Must be called in the
20927      * constructor of all subclasses
20928      * @method initFrame
20929      */
20930     initFrame: function() {
20931         this.createFrame();
20932     },
20933
20934     applyConfig: function() {
20935         Roo.dd.DDProxy.superclass.applyConfig.call(this);
20936
20937         this.resizeFrame = (this.config.resizeFrame !== false);
20938         this.centerFrame = (this.config.centerFrame);
20939         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
20940     },
20941
20942     /**
20943      * Resizes the drag frame to the dimensions of the clicked object, positions
20944      * it over the object, and finally displays it
20945      * @method showFrame
20946      * @param {int} iPageX X click position
20947      * @param {int} iPageY Y click position
20948      * @private
20949      */
20950     showFrame: function(iPageX, iPageY) {
20951         var el = this.getEl();
20952         var dragEl = this.getDragEl();
20953         var s = dragEl.style;
20954
20955         this._resizeProxy();
20956
20957         if (this.centerFrame) {
20958             this.setDelta( Math.round(parseInt(s.width,  10)/2),
20959                            Math.round(parseInt(s.height, 10)/2) );
20960         }
20961
20962         this.setDragElPos(iPageX, iPageY);
20963
20964         Roo.fly(dragEl).show();
20965     },
20966
20967     /**
20968      * The proxy is automatically resized to the dimensions of the linked
20969      * element when a drag is initiated, unless resizeFrame is set to false
20970      * @method _resizeProxy
20971      * @private
20972      */
20973     _resizeProxy: function() {
20974         if (this.resizeFrame) {
20975             var el = this.getEl();
20976             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
20977         }
20978     },
20979
20980     // overrides Roo.dd.DragDrop
20981     b4MouseDown: function(e) {
20982         var x = e.getPageX();
20983         var y = e.getPageY();
20984         this.autoOffset(x, y);
20985         this.setDragElPos(x, y);
20986     },
20987
20988     // overrides Roo.dd.DragDrop
20989     b4StartDrag: function(x, y) {
20990         // show the drag frame
20991         this.showFrame(x, y);
20992     },
20993
20994     // overrides Roo.dd.DragDrop
20995     b4EndDrag: function(e) {
20996         Roo.fly(this.getDragEl()).hide();
20997     },
20998
20999     // overrides Roo.dd.DragDrop
21000     // By default we try to move the element to the last location of the frame.
21001     // This is so that the default behavior mirrors that of Roo.dd.DD.
21002     endDrag: function(e) {
21003
21004         var lel = this.getEl();
21005         var del = this.getDragEl();
21006
21007         // Show the drag frame briefly so we can get its position
21008         del.style.visibility = "";
21009
21010         this.beforeMove();
21011         // Hide the linked element before the move to get around a Safari
21012         // rendering bug.
21013         lel.style.visibility = "hidden";
21014         Roo.dd.DDM.moveToEl(lel, del);
21015         del.style.visibility = "hidden";
21016         lel.style.visibility = "";
21017
21018         this.afterDrag();
21019     },
21020
21021     beforeMove : function(){
21022
21023     },
21024
21025     afterDrag : function(){
21026
21027     },
21028
21029     toString: function() {
21030         return ("DDProxy " + this.id);
21031     }
21032
21033 });
21034 /*
21035  * Based on:
21036  * Ext JS Library 1.1.1
21037  * Copyright(c) 2006-2007, Ext JS, LLC.
21038  *
21039  * Originally Released Under LGPL - original licence link has changed is not relivant.
21040  *
21041  * Fork - LGPL
21042  * <script type="text/javascript">
21043  */
21044
21045  /**
21046  * @class Roo.dd.DDTarget
21047  * A DragDrop implementation that does not move, but can be a drop
21048  * target.  You would get the same result by simply omitting implementation
21049  * for the event callbacks, but this way we reduce the processing cost of the
21050  * event listener and the callbacks.
21051  * @extends Roo.dd.DragDrop
21052  * @constructor
21053  * @param {String} id the id of the element that is a drop target
21054  * @param {String} sGroup the group of related DragDrop objects
21055  * @param {object} config an object containing configurable attributes
21056  *                 Valid properties for DDTarget in addition to those in
21057  *                 DragDrop:
21058  *                    none
21059  */
21060 Roo.dd.DDTarget = function(id, sGroup, config) {
21061     if (id) {
21062         this.initTarget(id, sGroup, config);
21063     }
21064     if (config.listeners || config.events) { 
21065        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
21066             listeners : config.listeners || {}, 
21067             events : config.events || {} 
21068         });    
21069     }
21070 };
21071
21072 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
21073 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
21074     toString: function() {
21075         return ("DDTarget " + this.id);
21076     }
21077 });
21078 /*
21079  * Based on:
21080  * Ext JS Library 1.1.1
21081  * Copyright(c) 2006-2007, Ext JS, LLC.
21082  *
21083  * Originally Released Under LGPL - original licence link has changed is not relivant.
21084  *
21085  * Fork - LGPL
21086  * <script type="text/javascript">
21087  */
21088  
21089
21090 /**
21091  * @class Roo.dd.ScrollManager
21092  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21093  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21094  * @singleton
21095  */
21096 Roo.dd.ScrollManager = function(){
21097     var ddm = Roo.dd.DragDropMgr;
21098     var els = {};
21099     var dragEl = null;
21100     var proc = {};
21101     
21102     
21103     
21104     var onStop = function(e){
21105         dragEl = null;
21106         clearProc();
21107     };
21108     
21109     var triggerRefresh = function(){
21110         if(ddm.dragCurrent){
21111              ddm.refreshCache(ddm.dragCurrent.groups);
21112         }
21113     };
21114     
21115     var doScroll = function(){
21116         if(ddm.dragCurrent){
21117             var dds = Roo.dd.ScrollManager;
21118             if(!dds.animate){
21119                 if(proc.el.scroll(proc.dir, dds.increment)){
21120                     triggerRefresh();
21121                 }
21122             }else{
21123                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21124             }
21125         }
21126     };
21127     
21128     var clearProc = function(){
21129         if(proc.id){
21130             clearInterval(proc.id);
21131         }
21132         proc.id = 0;
21133         proc.el = null;
21134         proc.dir = "";
21135     };
21136     
21137     var startProc = function(el, dir){
21138          Roo.log('scroll startproc');
21139         clearProc();
21140         proc.el = el;
21141         proc.dir = dir;
21142         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21143     };
21144     
21145     var onFire = function(e, isDrop){
21146        
21147         if(isDrop || !ddm.dragCurrent){ return; }
21148         var dds = Roo.dd.ScrollManager;
21149         if(!dragEl || dragEl != ddm.dragCurrent){
21150             dragEl = ddm.dragCurrent;
21151             // refresh regions on drag start
21152             dds.refreshCache();
21153         }
21154         
21155         var xy = Roo.lib.Event.getXY(e);
21156         var pt = new Roo.lib.Point(xy[0], xy[1]);
21157         for(var id in els){
21158             var el = els[id], r = el._region;
21159             if(r && r.contains(pt) && el.isScrollable()){
21160                 if(r.bottom - pt.y <= dds.thresh){
21161                     if(proc.el != el){
21162                         startProc(el, "down");
21163                     }
21164                     return;
21165                 }else if(r.right - pt.x <= dds.thresh){
21166                     if(proc.el != el){
21167                         startProc(el, "left");
21168                     }
21169                     return;
21170                 }else if(pt.y - r.top <= dds.thresh){
21171                     if(proc.el != el){
21172                         startProc(el, "up");
21173                     }
21174                     return;
21175                 }else if(pt.x - r.left <= dds.thresh){
21176                     if(proc.el != el){
21177                         startProc(el, "right");
21178                     }
21179                     return;
21180                 }
21181             }
21182         }
21183         clearProc();
21184     };
21185     
21186     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21187     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21188     
21189     return {
21190         /**
21191          * Registers new overflow element(s) to auto scroll
21192          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21193          */
21194         register : function(el){
21195             if(el instanceof Array){
21196                 for(var i = 0, len = el.length; i < len; i++) {
21197                         this.register(el[i]);
21198                 }
21199             }else{
21200                 el = Roo.get(el);
21201                 els[el.id] = el;
21202             }
21203             Roo.dd.ScrollManager.els = els;
21204         },
21205         
21206         /**
21207          * Unregisters overflow element(s) so they are no longer scrolled
21208          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21209          */
21210         unregister : function(el){
21211             if(el instanceof Array){
21212                 for(var i = 0, len = el.length; i < len; i++) {
21213                         this.unregister(el[i]);
21214                 }
21215             }else{
21216                 el = Roo.get(el);
21217                 delete els[el.id];
21218             }
21219         },
21220         
21221         /**
21222          * The number of pixels from the edge of a container the pointer needs to be to 
21223          * trigger scrolling (defaults to 25)
21224          * @type Number
21225          */
21226         thresh : 25,
21227         
21228         /**
21229          * The number of pixels to scroll in each scroll increment (defaults to 50)
21230          * @type Number
21231          */
21232         increment : 100,
21233         
21234         /**
21235          * The frequency of scrolls in milliseconds (defaults to 500)
21236          * @type Number
21237          */
21238         frequency : 500,
21239         
21240         /**
21241          * True to animate the scroll (defaults to true)
21242          * @type Boolean
21243          */
21244         animate: true,
21245         
21246         /**
21247          * The animation duration in seconds - 
21248          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21249          * @type Number
21250          */
21251         animDuration: .4,
21252         
21253         /**
21254          * Manually trigger a cache refresh.
21255          */
21256         refreshCache : function(){
21257             for(var id in els){
21258                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21259                     els[id]._region = els[id].getRegion();
21260                 }
21261             }
21262         }
21263     };
21264 }();/*
21265  * Based on:
21266  * Ext JS Library 1.1.1
21267  * Copyright(c) 2006-2007, Ext JS, LLC.
21268  *
21269  * Originally Released Under LGPL - original licence link has changed is not relivant.
21270  *
21271  * Fork - LGPL
21272  * <script type="text/javascript">
21273  */
21274  
21275
21276 /**
21277  * @class Roo.dd.Registry
21278  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21279  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21280  * @singleton
21281  */
21282 Roo.dd.Registry = function(){
21283     var elements = {}; 
21284     var handles = {}; 
21285     var autoIdSeed = 0;
21286
21287     var getId = function(el, autogen){
21288         if(typeof el == "string"){
21289             return el;
21290         }
21291         var id = el.id;
21292         if(!id && autogen !== false){
21293             id = "roodd-" + (++autoIdSeed);
21294             el.id = id;
21295         }
21296         return id;
21297     };
21298     
21299     return {
21300     /**
21301      * Register a drag drop element
21302      * @param {String|HTMLElement} element The id or DOM node to register
21303      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21304      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21305      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21306      * populated in the data object (if applicable):
21307      * <pre>
21308 Value      Description<br />
21309 ---------  ------------------------------------------<br />
21310 handles    Array of DOM nodes that trigger dragging<br />
21311            for the element being registered<br />
21312 isHandle   True if the element passed in triggers<br />
21313            dragging itself, else false
21314 </pre>
21315      */
21316         register : function(el, data){
21317             data = data || {};
21318             if(typeof el == "string"){
21319                 el = document.getElementById(el);
21320             }
21321             data.ddel = el;
21322             elements[getId(el)] = data;
21323             if(data.isHandle !== false){
21324                 handles[data.ddel.id] = data;
21325             }
21326             if(data.handles){
21327                 var hs = data.handles;
21328                 for(var i = 0, len = hs.length; i < len; i++){
21329                         handles[getId(hs[i])] = data;
21330                 }
21331             }
21332         },
21333
21334     /**
21335      * Unregister a drag drop element
21336      * @param {String|HTMLElement}  element The id or DOM node to unregister
21337      */
21338         unregister : function(el){
21339             var id = getId(el, false);
21340             var data = elements[id];
21341             if(data){
21342                 delete elements[id];
21343                 if(data.handles){
21344                     var hs = data.handles;
21345                     for(var i = 0, len = hs.length; i < len; i++){
21346                         delete handles[getId(hs[i], false)];
21347                     }
21348                 }
21349             }
21350         },
21351
21352     /**
21353      * Returns the handle registered for a DOM Node by id
21354      * @param {String|HTMLElement} id The DOM node or id to look up
21355      * @return {Object} handle The custom handle data
21356      */
21357         getHandle : function(id){
21358             if(typeof id != "string"){ // must be element?
21359                 id = id.id;
21360             }
21361             return handles[id];
21362         },
21363
21364     /**
21365      * Returns the handle that is registered for the DOM node that is the target of the event
21366      * @param {Event} e The event
21367      * @return {Object} handle The custom handle data
21368      */
21369         getHandleFromEvent : function(e){
21370             var t = Roo.lib.Event.getTarget(e);
21371             return t ? handles[t.id] : null;
21372         },
21373
21374     /**
21375      * Returns a custom data object that is registered for a DOM node by id
21376      * @param {String|HTMLElement} id The DOM node or id to look up
21377      * @return {Object} data The custom data
21378      */
21379         getTarget : function(id){
21380             if(typeof id != "string"){ // must be element?
21381                 id = id.id;
21382             }
21383             return elements[id];
21384         },
21385
21386     /**
21387      * Returns a custom data object that is registered for the DOM node that is the target of the event
21388      * @param {Event} e The event
21389      * @return {Object} data The custom data
21390      */
21391         getTargetFromEvent : function(e){
21392             var t = Roo.lib.Event.getTarget(e);
21393             return t ? elements[t.id] || handles[t.id] : null;
21394         }
21395     };
21396 }();/*
21397  * Based on:
21398  * Ext JS Library 1.1.1
21399  * Copyright(c) 2006-2007, Ext JS, LLC.
21400  *
21401  * Originally Released Under LGPL - original licence link has changed is not relivant.
21402  *
21403  * Fork - LGPL
21404  * <script type="text/javascript">
21405  */
21406  
21407
21408 /**
21409  * @class Roo.dd.StatusProxy
21410  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21411  * default drag proxy used by all Roo.dd components.
21412  * @constructor
21413  * @param {Object} config
21414  */
21415 Roo.dd.StatusProxy = function(config){
21416     Roo.apply(this, config);
21417     this.id = this.id || Roo.id();
21418     this.el = new Roo.Layer({
21419         dh: {
21420             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21421                 {tag: "div", cls: "x-dd-drop-icon"},
21422                 {tag: "div", cls: "x-dd-drag-ghost"}
21423             ]
21424         }, 
21425         shadow: !config || config.shadow !== false
21426     });
21427     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21428     this.dropStatus = this.dropNotAllowed;
21429 };
21430
21431 Roo.dd.StatusProxy.prototype = {
21432     /**
21433      * @cfg {String} dropAllowed
21434      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
21435      */
21436     dropAllowed : "x-dd-drop-ok",
21437     /**
21438      * @cfg {String} dropNotAllowed
21439      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
21440      */
21441     dropNotAllowed : "x-dd-drop-nodrop",
21442
21443     /**
21444      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
21445      * over the current target element.
21446      * @param {String} cssClass The css class for the new drop status indicator image
21447      */
21448     setStatus : function(cssClass){
21449         cssClass = cssClass || this.dropNotAllowed;
21450         if(this.dropStatus != cssClass){
21451             this.el.replaceClass(this.dropStatus, cssClass);
21452             this.dropStatus = cssClass;
21453         }
21454     },
21455
21456     /**
21457      * Resets the status indicator to the default dropNotAllowed value
21458      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
21459      */
21460     reset : function(clearGhost){
21461         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
21462         this.dropStatus = this.dropNotAllowed;
21463         if(clearGhost){
21464             this.ghost.update("");
21465         }
21466     },
21467
21468     /**
21469      * Updates the contents of the ghost element
21470      * @param {String} html The html that will replace the current innerHTML of the ghost element
21471      */
21472     update : function(html){
21473         if(typeof html == "string"){
21474             this.ghost.update(html);
21475         }else{
21476             this.ghost.update("");
21477             html.style.margin = "0";
21478             this.ghost.dom.appendChild(html);
21479         }
21480         // ensure float = none set?? cant remember why though.
21481         var el = this.ghost.dom.firstChild;
21482                 if(el){
21483                         Roo.fly(el).setStyle('float', 'none');
21484                 }
21485     },
21486     
21487     /**
21488      * Returns the underlying proxy {@link Roo.Layer}
21489      * @return {Roo.Layer} el
21490     */
21491     getEl : function(){
21492         return this.el;
21493     },
21494
21495     /**
21496      * Returns the ghost element
21497      * @return {Roo.Element} el
21498      */
21499     getGhost : function(){
21500         return this.ghost;
21501     },
21502
21503     /**
21504      * Hides the proxy
21505      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
21506      */
21507     hide : function(clear){
21508         this.el.hide();
21509         if(clear){
21510             this.reset(true);
21511         }
21512     },
21513
21514     /**
21515      * Stops the repair animation if it's currently running
21516      */
21517     stop : function(){
21518         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
21519             this.anim.stop();
21520         }
21521     },
21522
21523     /**
21524      * Displays this proxy
21525      */
21526     show : function(){
21527         this.el.show();
21528     },
21529
21530     /**
21531      * Force the Layer to sync its shadow and shim positions to the element
21532      */
21533     sync : function(){
21534         this.el.sync();
21535     },
21536
21537     /**
21538      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
21539      * invalid drop operation by the item being dragged.
21540      * @param {Array} xy The XY position of the element ([x, y])
21541      * @param {Function} callback The function to call after the repair is complete
21542      * @param {Object} scope The scope in which to execute the callback
21543      */
21544     repair : function(xy, callback, scope){
21545         this.callback = callback;
21546         this.scope = scope;
21547         if(xy && this.animRepair !== false){
21548             this.el.addClass("x-dd-drag-repair");
21549             this.el.hideUnders(true);
21550             this.anim = this.el.shift({
21551                 duration: this.repairDuration || .5,
21552                 easing: 'easeOut',
21553                 xy: xy,
21554                 stopFx: true,
21555                 callback: this.afterRepair,
21556                 scope: this
21557             });
21558         }else{
21559             this.afterRepair();
21560         }
21561     },
21562
21563     // private
21564     afterRepair : function(){
21565         this.hide(true);
21566         if(typeof this.callback == "function"){
21567             this.callback.call(this.scope || this);
21568         }
21569         this.callback = null;
21570         this.scope = null;
21571     }
21572 };/*
21573  * Based on:
21574  * Ext JS Library 1.1.1
21575  * Copyright(c) 2006-2007, Ext JS, LLC.
21576  *
21577  * Originally Released Under LGPL - original licence link has changed is not relivant.
21578  *
21579  * Fork - LGPL
21580  * <script type="text/javascript">
21581  */
21582
21583 /**
21584  * @class Roo.dd.DragSource
21585  * @extends Roo.dd.DDProxy
21586  * A simple class that provides the basic implementation needed to make any element draggable.
21587  * @constructor
21588  * @param {String/HTMLElement/Element} el The container element
21589  * @param {Object} config
21590  */
21591 Roo.dd.DragSource = function(el, config){
21592     this.el = Roo.get(el);
21593     this.dragData = {};
21594     
21595     Roo.apply(this, config);
21596     
21597     if(!this.proxy){
21598         this.proxy = new Roo.dd.StatusProxy();
21599     }
21600
21601     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
21602           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
21603     
21604     this.dragging = false;
21605 };
21606
21607 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
21608     /**
21609      * @cfg {String} dropAllowed
21610      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21611      */
21612     dropAllowed : "x-dd-drop-ok",
21613     /**
21614      * @cfg {String} dropNotAllowed
21615      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21616      */
21617     dropNotAllowed : "x-dd-drop-nodrop",
21618
21619     /**
21620      * Returns the data object associated with this drag source
21621      * @return {Object} data An object containing arbitrary data
21622      */
21623     getDragData : function(e){
21624         return this.dragData;
21625     },
21626
21627     // private
21628     onDragEnter : function(e, id){
21629         var target = Roo.dd.DragDropMgr.getDDById(id);
21630         this.cachedTarget = target;
21631         if(this.beforeDragEnter(target, e, id) !== false){
21632             if(target.isNotifyTarget){
21633                 var status = target.notifyEnter(this, e, this.dragData);
21634                 this.proxy.setStatus(status);
21635             }else{
21636                 this.proxy.setStatus(this.dropAllowed);
21637             }
21638             
21639             if(this.afterDragEnter){
21640                 /**
21641                  * An empty function by default, but provided so that you can perform a custom action
21642                  * when the dragged item enters the drop target by providing an implementation.
21643                  * @param {Roo.dd.DragDrop} target The drop target
21644                  * @param {Event} e The event object
21645                  * @param {String} id The id of the dragged element
21646                  * @method afterDragEnter
21647                  */
21648                 this.afterDragEnter(target, e, id);
21649             }
21650         }
21651     },
21652
21653     /**
21654      * An empty function by default, but provided so that you can perform a custom action
21655      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
21656      * @param {Roo.dd.DragDrop} target The drop target
21657      * @param {Event} e The event object
21658      * @param {String} id The id of the dragged element
21659      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21660      */
21661     beforeDragEnter : function(target, e, id){
21662         return true;
21663     },
21664
21665     // private
21666     alignElWithMouse: function() {
21667         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
21668         this.proxy.sync();
21669     },
21670
21671     // private
21672     onDragOver : function(e, id){
21673         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21674         if(this.beforeDragOver(target, e, id) !== false){
21675             if(target.isNotifyTarget){
21676                 var status = target.notifyOver(this, e, this.dragData);
21677                 this.proxy.setStatus(status);
21678             }
21679
21680             if(this.afterDragOver){
21681                 /**
21682                  * An empty function by default, but provided so that you can perform a custom action
21683                  * while the dragged item is over the drop target by providing an implementation.
21684                  * @param {Roo.dd.DragDrop} target The drop target
21685                  * @param {Event} e The event object
21686                  * @param {String} id The id of the dragged element
21687                  * @method afterDragOver
21688                  */
21689                 this.afterDragOver(target, e, id);
21690             }
21691         }
21692     },
21693
21694     /**
21695      * An empty function by default, but provided so that you can perform a custom action
21696      * while the dragged item is over the drop target and optionally cancel the onDragOver.
21697      * @param {Roo.dd.DragDrop} target The drop target
21698      * @param {Event} e The event object
21699      * @param {String} id The id of the dragged element
21700      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21701      */
21702     beforeDragOver : function(target, e, id){
21703         return true;
21704     },
21705
21706     // private
21707     onDragOut : function(e, id){
21708         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21709         if(this.beforeDragOut(target, e, id) !== false){
21710             if(target.isNotifyTarget){
21711                 target.notifyOut(this, e, this.dragData);
21712             }
21713             this.proxy.reset();
21714             if(this.afterDragOut){
21715                 /**
21716                  * An empty function by default, but provided so that you can perform a custom action
21717                  * after the dragged item is dragged out of the target without dropping.
21718                  * @param {Roo.dd.DragDrop} target The drop target
21719                  * @param {Event} e The event object
21720                  * @param {String} id The id of the dragged element
21721                  * @method afterDragOut
21722                  */
21723                 this.afterDragOut(target, e, id);
21724             }
21725         }
21726         this.cachedTarget = null;
21727     },
21728
21729     /**
21730      * An empty function by default, but provided so that you can perform a custom action before the dragged
21731      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
21732      * @param {Roo.dd.DragDrop} target The drop target
21733      * @param {Event} e The event object
21734      * @param {String} id The id of the dragged element
21735      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21736      */
21737     beforeDragOut : function(target, e, id){
21738         return true;
21739     },
21740     
21741     // private
21742     onDragDrop : function(e, id){
21743         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21744         if(this.beforeDragDrop(target, e, id) !== false){
21745             if(target.isNotifyTarget){
21746                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
21747                     this.onValidDrop(target, e, id);
21748                 }else{
21749                     this.onInvalidDrop(target, e, id);
21750                 }
21751             }else{
21752                 this.onValidDrop(target, e, id);
21753             }
21754             
21755             if(this.afterDragDrop){
21756                 /**
21757                  * An empty function by default, but provided so that you can perform a custom action
21758                  * after a valid drag drop has occurred by providing an implementation.
21759                  * @param {Roo.dd.DragDrop} target The drop target
21760                  * @param {Event} e The event object
21761                  * @param {String} id The id of the dropped element
21762                  * @method afterDragDrop
21763                  */
21764                 this.afterDragDrop(target, e, id);
21765             }
21766         }
21767         delete this.cachedTarget;
21768     },
21769
21770     /**
21771      * An empty function by default, but provided so that you can perform a custom action before the dragged
21772      * item is dropped onto the target and optionally cancel the onDragDrop.
21773      * @param {Roo.dd.DragDrop} target The drop target
21774      * @param {Event} e The event object
21775      * @param {String} id The id of the dragged element
21776      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
21777      */
21778     beforeDragDrop : function(target, e, id){
21779         return true;
21780     },
21781
21782     // private
21783     onValidDrop : function(target, e, id){
21784         this.hideProxy();
21785         if(this.afterValidDrop){
21786             /**
21787              * An empty function by default, but provided so that you can perform a custom action
21788              * after a valid drop has occurred by providing an implementation.
21789              * @param {Object} target The target DD 
21790              * @param {Event} e The event object
21791              * @param {String} id The id of the dropped element
21792              * @method afterInvalidDrop
21793              */
21794             this.afterValidDrop(target, e, id);
21795         }
21796     },
21797
21798     // private
21799     getRepairXY : function(e, data){
21800         return this.el.getXY();  
21801     },
21802
21803     // private
21804     onInvalidDrop : function(target, e, id){
21805         this.beforeInvalidDrop(target, e, id);
21806         if(this.cachedTarget){
21807             if(this.cachedTarget.isNotifyTarget){
21808                 this.cachedTarget.notifyOut(this, e, this.dragData);
21809             }
21810             this.cacheTarget = null;
21811         }
21812         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
21813
21814         if(this.afterInvalidDrop){
21815             /**
21816              * An empty function by default, but provided so that you can perform a custom action
21817              * after an invalid drop has occurred by providing an implementation.
21818              * @param {Event} e The event object
21819              * @param {String} id The id of the dropped element
21820              * @method afterInvalidDrop
21821              */
21822             this.afterInvalidDrop(e, id);
21823         }
21824     },
21825
21826     // private
21827     afterRepair : function(){
21828         if(Roo.enableFx){
21829             this.el.highlight(this.hlColor || "c3daf9");
21830         }
21831         this.dragging = false;
21832     },
21833
21834     /**
21835      * An empty function by default, but provided so that you can perform a custom action after an invalid
21836      * drop has occurred.
21837      * @param {Roo.dd.DragDrop} target The drop target
21838      * @param {Event} e The event object
21839      * @param {String} id The id of the dragged element
21840      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
21841      */
21842     beforeInvalidDrop : function(target, e, id){
21843         return true;
21844     },
21845
21846     // private
21847     handleMouseDown : function(e){
21848         if(this.dragging) {
21849             return;
21850         }
21851         var data = this.getDragData(e);
21852         if(data && this.onBeforeDrag(data, e) !== false){
21853             this.dragData = data;
21854             this.proxy.stop();
21855             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
21856         } 
21857     },
21858
21859     /**
21860      * An empty function by default, but provided so that you can perform a custom action before the initial
21861      * drag event begins and optionally cancel it.
21862      * @param {Object} data An object containing arbitrary data to be shared with drop targets
21863      * @param {Event} e The event object
21864      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21865      */
21866     onBeforeDrag : function(data, e){
21867         return true;
21868     },
21869
21870     /**
21871      * An empty function by default, but provided so that you can perform a custom action once the initial
21872      * drag event has begun.  The drag cannot be canceled from this function.
21873      * @param {Number} x The x position of the click on the dragged object
21874      * @param {Number} y The y position of the click on the dragged object
21875      */
21876     onStartDrag : Roo.emptyFn,
21877
21878     // private - YUI override
21879     startDrag : function(x, y){
21880         this.proxy.reset();
21881         this.dragging = true;
21882         this.proxy.update("");
21883         this.onInitDrag(x, y);
21884         this.proxy.show();
21885     },
21886
21887     // private
21888     onInitDrag : function(x, y){
21889         var clone = this.el.dom.cloneNode(true);
21890         clone.id = Roo.id(); // prevent duplicate ids
21891         this.proxy.update(clone);
21892         this.onStartDrag(x, y);
21893         return true;
21894     },
21895
21896     /**
21897      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
21898      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
21899      */
21900     getProxy : function(){
21901         return this.proxy;  
21902     },
21903
21904     /**
21905      * Hides the drag source's {@link Roo.dd.StatusProxy}
21906      */
21907     hideProxy : function(){
21908         this.proxy.hide();  
21909         this.proxy.reset(true);
21910         this.dragging = false;
21911     },
21912
21913     // private
21914     triggerCacheRefresh : function(){
21915         Roo.dd.DDM.refreshCache(this.groups);
21916     },
21917
21918     // private - override to prevent hiding
21919     b4EndDrag: function(e) {
21920     },
21921
21922     // private - override to prevent moving
21923     endDrag : function(e){
21924         this.onEndDrag(this.dragData, e);
21925     },
21926
21927     // private
21928     onEndDrag : function(data, e){
21929     },
21930     
21931     // private - pin to cursor
21932     autoOffset : function(x, y) {
21933         this.setDelta(-12, -20);
21934     }    
21935 });/*
21936  * Based on:
21937  * Ext JS Library 1.1.1
21938  * Copyright(c) 2006-2007, Ext JS, LLC.
21939  *
21940  * Originally Released Under LGPL - original licence link has changed is not relivant.
21941  *
21942  * Fork - LGPL
21943  * <script type="text/javascript">
21944  */
21945
21946
21947 /**
21948  * @class Roo.dd.DropTarget
21949  * @extends Roo.dd.DDTarget
21950  * A simple class that provides the basic implementation needed to make any element a drop target that can have
21951  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
21952  * @constructor
21953  * @param {String/HTMLElement/Element} el The container element
21954  * @param {Object} config
21955  */
21956 Roo.dd.DropTarget = function(el, config){
21957     this.el = Roo.get(el);
21958     
21959     var listeners = false; ;
21960     if (config && config.listeners) {
21961         listeners= config.listeners;
21962         delete config.listeners;
21963     }
21964     Roo.apply(this, config);
21965     
21966     if(this.containerScroll){
21967         Roo.dd.ScrollManager.register(this.el);
21968     }
21969     this.addEvents( {
21970          /**
21971          * @scope Roo.dd.DropTarget
21972          */
21973          
21974          /**
21975          * @event enter
21976          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
21977          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
21978          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
21979          * 
21980          * IMPORTANT : it should set this.overClass and this.dropAllowed
21981          * 
21982          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21983          * @param {Event} e The event
21984          * @param {Object} data An object containing arbitrary data supplied by the drag source
21985          */
21986         "enter" : true,
21987         
21988          /**
21989          * @event over
21990          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
21991          * This method will be called on every mouse movement while the drag source is over the drop target.
21992          * This default implementation simply returns the dropAllowed config value.
21993          * 
21994          * IMPORTANT : it should set this.dropAllowed
21995          * 
21996          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21997          * @param {Event} e The event
21998          * @param {Object} data An object containing arbitrary data supplied by the drag source
21999          
22000          */
22001         "over" : true,
22002         /**
22003          * @event out
22004          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
22005          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
22006          * overClass (if any) from the drop element.
22007          * 
22008          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22009          * @param {Event} e The event
22010          * @param {Object} data An object containing arbitrary data supplied by the drag source
22011          */
22012          "out" : true,
22013          
22014         /**
22015          * @event drop
22016          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
22017          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
22018          * implementation that does something to process the drop event and returns true so that the drag source's
22019          * repair action does not run.
22020          * 
22021          * IMPORTANT : it should set this.success
22022          * 
22023          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22024          * @param {Event} e The event
22025          * @param {Object} data An object containing arbitrary data supplied by the drag source
22026         */
22027          "drop" : true
22028     });
22029             
22030      
22031     Roo.dd.DropTarget.superclass.constructor.call(  this, 
22032         this.el.dom, 
22033         this.ddGroup || this.group,
22034         {
22035             isTarget: true,
22036             listeners : listeners || {} 
22037            
22038         
22039         }
22040     );
22041
22042 };
22043
22044 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
22045     /**
22046      * @cfg {String} overClass
22047      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
22048      */
22049      /**
22050      * @cfg {String} ddGroup
22051      * The drag drop group to handle drop events for
22052      */
22053      
22054     /**
22055      * @cfg {String} dropAllowed
22056      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22057      */
22058     dropAllowed : "x-dd-drop-ok",
22059     /**
22060      * @cfg {String} dropNotAllowed
22061      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22062      */
22063     dropNotAllowed : "x-dd-drop-nodrop",
22064     /**
22065      * @cfg {boolean} success
22066      * set this after drop listener.. 
22067      */
22068     success : false,
22069     /**
22070      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
22071      * if the drop point is valid for over/enter..
22072      */
22073     valid : false,
22074     // private
22075     isTarget : true,
22076
22077     // private
22078     isNotifyTarget : true,
22079     
22080     /**
22081      * @hide
22082      */
22083     notifyEnter : function(dd, e, data)
22084     {
22085         this.valid = true;
22086         this.fireEvent('enter', dd, e, data);
22087         if(this.overClass){
22088             this.el.addClass(this.overClass);
22089         }
22090         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22091             this.valid ? this.dropAllowed : this.dropNotAllowed
22092         );
22093     },
22094
22095     /**
22096      * @hide
22097      */
22098     notifyOver : function(dd, e, data)
22099     {
22100         this.valid = true;
22101         this.fireEvent('over', dd, e, data);
22102         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22103             this.valid ? this.dropAllowed : this.dropNotAllowed
22104         );
22105     },
22106
22107     /**
22108      * @hide
22109      */
22110     notifyOut : function(dd, e, data)
22111     {
22112         this.fireEvent('out', dd, e, data);
22113         if(this.overClass){
22114             this.el.removeClass(this.overClass);
22115         }
22116     },
22117
22118     /**
22119      * @hide
22120      */
22121     notifyDrop : function(dd, e, data)
22122     {
22123         this.success = false;
22124         this.fireEvent('drop', dd, e, data);
22125         return this.success;
22126     }
22127 });/*
22128  * Based on:
22129  * Ext JS Library 1.1.1
22130  * Copyright(c) 2006-2007, Ext JS, LLC.
22131  *
22132  * Originally Released Under LGPL - original licence link has changed is not relivant.
22133  *
22134  * Fork - LGPL
22135  * <script type="text/javascript">
22136  */
22137
22138
22139 /**
22140  * @class Roo.dd.DragZone
22141  * @extends Roo.dd.DragSource
22142  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22143  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22144  * @constructor
22145  * @param {String/HTMLElement/Element} el The container element
22146  * @param {Object} config
22147  */
22148 Roo.dd.DragZone = function(el, config){
22149     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22150     if(this.containerScroll){
22151         Roo.dd.ScrollManager.register(this.el);
22152     }
22153 };
22154
22155 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22156     /**
22157      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22158      * for auto scrolling during drag operations.
22159      */
22160     /**
22161      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22162      * method after a failed drop (defaults to "c3daf9" - light blue)
22163      */
22164
22165     /**
22166      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22167      * for a valid target to drag based on the mouse down. Override this method
22168      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22169      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22170      * @param {EventObject} e The mouse down event
22171      * @return {Object} The dragData
22172      */
22173     getDragData : function(e){
22174         return Roo.dd.Registry.getHandleFromEvent(e);
22175     },
22176     
22177     /**
22178      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22179      * this.dragData.ddel
22180      * @param {Number} x The x position of the click on the dragged object
22181      * @param {Number} y The y position of the click on the dragged object
22182      * @return {Boolean} true to continue the drag, false to cancel
22183      */
22184     onInitDrag : function(x, y){
22185         this.proxy.update(this.dragData.ddel.cloneNode(true));
22186         this.onStartDrag(x, y);
22187         return true;
22188     },
22189     
22190     /**
22191      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22192      */
22193     afterRepair : function(){
22194         if(Roo.enableFx){
22195             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22196         }
22197         this.dragging = false;
22198     },
22199
22200     /**
22201      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22202      * the XY of this.dragData.ddel
22203      * @param {EventObject} e The mouse up event
22204      * @return {Array} The xy location (e.g. [100, 200])
22205      */
22206     getRepairXY : function(e){
22207         return Roo.Element.fly(this.dragData.ddel).getXY();  
22208     }
22209 });/*
22210  * Based on:
22211  * Ext JS Library 1.1.1
22212  * Copyright(c) 2006-2007, Ext JS, LLC.
22213  *
22214  * Originally Released Under LGPL - original licence link has changed is not relivant.
22215  *
22216  * Fork - LGPL
22217  * <script type="text/javascript">
22218  */
22219 /**
22220  * @class Roo.dd.DropZone
22221  * @extends Roo.dd.DropTarget
22222  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22223  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22224  * @constructor
22225  * @param {String/HTMLElement/Element} el The container element
22226  * @param {Object} config
22227  */
22228 Roo.dd.DropZone = function(el, config){
22229     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22230 };
22231
22232 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22233     /**
22234      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22235      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22236      * provide your own custom lookup.
22237      * @param {Event} e The event
22238      * @return {Object} data The custom data
22239      */
22240     getTargetFromEvent : function(e){
22241         return Roo.dd.Registry.getTargetFromEvent(e);
22242     },
22243
22244     /**
22245      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22246      * that it has registered.  This method has no default implementation and should be overridden to provide
22247      * node-specific processing if necessary.
22248      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22249      * {@link #getTargetFromEvent} for this node)
22250      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22251      * @param {Event} e The event
22252      * @param {Object} data An object containing arbitrary data supplied by the drag source
22253      */
22254     onNodeEnter : function(n, dd, e, data){
22255         
22256     },
22257
22258     /**
22259      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22260      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22261      * overridden to provide the proper feedback.
22262      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22263      * {@link #getTargetFromEvent} for this node)
22264      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22265      * @param {Event} e The event
22266      * @param {Object} data An object containing arbitrary data supplied by the drag source
22267      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22268      * underlying {@link Roo.dd.StatusProxy} can be updated
22269      */
22270     onNodeOver : function(n, dd, e, data){
22271         return this.dropAllowed;
22272     },
22273
22274     /**
22275      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22276      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22277      * node-specific processing if necessary.
22278      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22279      * {@link #getTargetFromEvent} for this node)
22280      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22281      * @param {Event} e The event
22282      * @param {Object} data An object containing arbitrary data supplied by the drag source
22283      */
22284     onNodeOut : function(n, dd, e, data){
22285         
22286     },
22287
22288     /**
22289      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22290      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22291      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22292      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22293      * {@link #getTargetFromEvent} for this node)
22294      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22295      * @param {Event} e The event
22296      * @param {Object} data An object containing arbitrary data supplied by the drag source
22297      * @return {Boolean} True if the drop was valid, else false
22298      */
22299     onNodeDrop : function(n, dd, e, data){
22300         return false;
22301     },
22302
22303     /**
22304      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22305      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22306      * it should be overridden to provide the proper feedback if necessary.
22307      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22308      * @param {Event} e The event
22309      * @param {Object} data An object containing arbitrary data supplied by the drag source
22310      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22311      * underlying {@link Roo.dd.StatusProxy} can be updated
22312      */
22313     onContainerOver : function(dd, e, data){
22314         return this.dropNotAllowed;
22315     },
22316
22317     /**
22318      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22319      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22320      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22321      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22322      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22323      * @param {Event} e The event
22324      * @param {Object} data An object containing arbitrary data supplied by the drag source
22325      * @return {Boolean} True if the drop was valid, else false
22326      */
22327     onContainerDrop : function(dd, e, data){
22328         return false;
22329     },
22330
22331     /**
22332      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22333      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22334      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22335      * you should override this method and provide a custom implementation.
22336      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22337      * @param {Event} e The event
22338      * @param {Object} data An object containing arbitrary data supplied by the drag source
22339      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22340      * underlying {@link Roo.dd.StatusProxy} can be updated
22341      */
22342     notifyEnter : function(dd, e, data){
22343         return this.dropNotAllowed;
22344     },
22345
22346     /**
22347      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22348      * This method will be called on every mouse movement while the drag source is over the drop zone.
22349      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22350      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22351      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22352      * registered node, it will call {@link #onContainerOver}.
22353      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22354      * @param {Event} e The event
22355      * @param {Object} data An object containing arbitrary data supplied by the drag source
22356      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22357      * underlying {@link Roo.dd.StatusProxy} can be updated
22358      */
22359     notifyOver : function(dd, e, data){
22360         var n = this.getTargetFromEvent(e);
22361         if(!n){ // not over valid drop target
22362             if(this.lastOverNode){
22363                 this.onNodeOut(this.lastOverNode, dd, e, data);
22364                 this.lastOverNode = null;
22365             }
22366             return this.onContainerOver(dd, e, data);
22367         }
22368         if(this.lastOverNode != n){
22369             if(this.lastOverNode){
22370                 this.onNodeOut(this.lastOverNode, dd, e, data);
22371             }
22372             this.onNodeEnter(n, dd, e, data);
22373             this.lastOverNode = n;
22374         }
22375         return this.onNodeOver(n, dd, e, data);
22376     },
22377
22378     /**
22379      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22380      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22381      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22382      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22383      * @param {Event} e The event
22384      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22385      */
22386     notifyOut : function(dd, e, data){
22387         if(this.lastOverNode){
22388             this.onNodeOut(this.lastOverNode, dd, e, data);
22389             this.lastOverNode = null;
22390         }
22391     },
22392
22393     /**
22394      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22395      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22396      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22397      * otherwise it will call {@link #onContainerDrop}.
22398      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22399      * @param {Event} e The event
22400      * @param {Object} data An object containing arbitrary data supplied by the drag source
22401      * @return {Boolean} True if the drop was valid, else false
22402      */
22403     notifyDrop : function(dd, e, data){
22404         if(this.lastOverNode){
22405             this.onNodeOut(this.lastOverNode, dd, e, data);
22406             this.lastOverNode = null;
22407         }
22408         var n = this.getTargetFromEvent(e);
22409         return n ?
22410             this.onNodeDrop(n, dd, e, data) :
22411             this.onContainerDrop(dd, e, data);
22412     },
22413
22414     // private
22415     triggerCacheRefresh : function(){
22416         Roo.dd.DDM.refreshCache(this.groups);
22417     }  
22418 });/*
22419  * Based on:
22420  * Ext JS Library 1.1.1
22421  * Copyright(c) 2006-2007, Ext JS, LLC.
22422  *
22423  * Originally Released Under LGPL - original licence link has changed is not relivant.
22424  *
22425  * Fork - LGPL
22426  * <script type="text/javascript">
22427  */
22428
22429
22430 /**
22431  * @class Roo.data.SortTypes
22432  * @singleton
22433  * Defines the default sorting (casting?) comparison functions used when sorting data.
22434  */
22435 Roo.data.SortTypes = {
22436     /**
22437      * Default sort that does nothing
22438      * @param {Mixed} s The value being converted
22439      * @return {Mixed} The comparison value
22440      */
22441     none : function(s){
22442         return s;
22443     },
22444     
22445     /**
22446      * The regular expression used to strip tags
22447      * @type {RegExp}
22448      * @property
22449      */
22450     stripTagsRE : /<\/?[^>]+>/gi,
22451     
22452     /**
22453      * Strips all HTML tags to sort on text only
22454      * @param {Mixed} s The value being converted
22455      * @return {String} The comparison value
22456      */
22457     asText : function(s){
22458         return String(s).replace(this.stripTagsRE, "");
22459     },
22460     
22461     /**
22462      * Strips all HTML tags to sort on text only - Case insensitive
22463      * @param {Mixed} s The value being converted
22464      * @return {String} The comparison value
22465      */
22466     asUCText : function(s){
22467         return String(s).toUpperCase().replace(this.stripTagsRE, "");
22468     },
22469     
22470     /**
22471      * Case insensitive string
22472      * @param {Mixed} s The value being converted
22473      * @return {String} The comparison value
22474      */
22475     asUCString : function(s) {
22476         return String(s).toUpperCase();
22477     },
22478     
22479     /**
22480      * Date sorting
22481      * @param {Mixed} s The value being converted
22482      * @return {Number} The comparison value
22483      */
22484     asDate : function(s) {
22485         if(!s){
22486             return 0;
22487         }
22488         if(s instanceof Date){
22489             return s.getTime();
22490         }
22491         return Date.parse(String(s));
22492     },
22493     
22494     /**
22495      * Float sorting
22496      * @param {Mixed} s The value being converted
22497      * @return {Float} The comparison value
22498      */
22499     asFloat : function(s) {
22500         var val = parseFloat(String(s).replace(/,/g, ""));
22501         if(isNaN(val)) {
22502             val = 0;
22503         }
22504         return val;
22505     },
22506     
22507     /**
22508      * Integer sorting
22509      * @param {Mixed} s The value being converted
22510      * @return {Number} The comparison value
22511      */
22512     asInt : function(s) {
22513         var val = parseInt(String(s).replace(/,/g, ""));
22514         if(isNaN(val)) {
22515             val = 0;
22516         }
22517         return val;
22518     }
22519 };/*
22520  * Based on:
22521  * Ext JS Library 1.1.1
22522  * Copyright(c) 2006-2007, Ext JS, LLC.
22523  *
22524  * Originally Released Under LGPL - original licence link has changed is not relivant.
22525  *
22526  * Fork - LGPL
22527  * <script type="text/javascript">
22528  */
22529
22530 /**
22531 * @class Roo.data.Record
22532  * Instances of this class encapsulate both record <em>definition</em> information, and record
22533  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
22534  * to access Records cached in an {@link Roo.data.Store} object.<br>
22535  * <p>
22536  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
22537  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
22538  * objects.<br>
22539  * <p>
22540  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
22541  * @constructor
22542  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
22543  * {@link #create}. The parameters are the same.
22544  * @param {Array} data An associative Array of data values keyed by the field name.
22545  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
22546  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
22547  * not specified an integer id is generated.
22548  */
22549 Roo.data.Record = function(data, id){
22550     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
22551     this.data = data;
22552 };
22553
22554 /**
22555  * Generate a constructor for a specific record layout.
22556  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
22557  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
22558  * Each field definition object may contain the following properties: <ul>
22559  * <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,
22560  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
22561  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
22562  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
22563  * is being used, then this is a string containing the javascript expression to reference the data relative to 
22564  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
22565  * to the data item relative to the record element. If the mapping expression is the same as the field name,
22566  * this may be omitted.</p></li>
22567  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
22568  * <ul><li>auto (Default, implies no conversion)</li>
22569  * <li>string</li>
22570  * <li>int</li>
22571  * <li>float</li>
22572  * <li>boolean</li>
22573  * <li>date</li></ul></p></li>
22574  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
22575  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
22576  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
22577  * by the Reader into an object that will be stored in the Record. It is passed the
22578  * following parameters:<ul>
22579  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
22580  * </ul></p></li>
22581  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
22582  * </ul>
22583  * <br>usage:<br><pre><code>
22584 var TopicRecord = Roo.data.Record.create(
22585     {name: 'title', mapping: 'topic_title'},
22586     {name: 'author', mapping: 'username'},
22587     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
22588     {name: 'lastPost', mapping: 'post_time', type: 'date'},
22589     {name: 'lastPoster', mapping: 'user2'},
22590     {name: 'excerpt', mapping: 'post_text'}
22591 );
22592
22593 var myNewRecord = new TopicRecord({
22594     title: 'Do my job please',
22595     author: 'noobie',
22596     totalPosts: 1,
22597     lastPost: new Date(),
22598     lastPoster: 'Animal',
22599     excerpt: 'No way dude!'
22600 });
22601 myStore.add(myNewRecord);
22602 </code></pre>
22603  * @method create
22604  * @static
22605  */
22606 Roo.data.Record.create = function(o){
22607     var f = function(){
22608         f.superclass.constructor.apply(this, arguments);
22609     };
22610     Roo.extend(f, Roo.data.Record);
22611     var p = f.prototype;
22612     p.fields = new Roo.util.MixedCollection(false, function(field){
22613         return field.name;
22614     });
22615     for(var i = 0, len = o.length; i < len; i++){
22616         p.fields.add(new Roo.data.Field(o[i]));
22617     }
22618     f.getField = function(name){
22619         return p.fields.get(name);  
22620     };
22621     return f;
22622 };
22623
22624 Roo.data.Record.AUTO_ID = 1000;
22625 Roo.data.Record.EDIT = 'edit';
22626 Roo.data.Record.REJECT = 'reject';
22627 Roo.data.Record.COMMIT = 'commit';
22628
22629 Roo.data.Record.prototype = {
22630     /**
22631      * Readonly flag - true if this record has been modified.
22632      * @type Boolean
22633      */
22634     dirty : false,
22635     editing : false,
22636     error: null,
22637     modified: null,
22638
22639     // private
22640     join : function(store){
22641         this.store = store;
22642     },
22643
22644     /**
22645      * Set the named field to the specified value.
22646      * @param {String} name The name of the field to set.
22647      * @param {Object} value The value to set the field to.
22648      */
22649     set : function(name, value){
22650         if(this.data[name] == value){
22651             return;
22652         }
22653         this.dirty = true;
22654         if(!this.modified){
22655             this.modified = {};
22656         }
22657         if(typeof this.modified[name] == 'undefined'){
22658             this.modified[name] = this.data[name];
22659         }
22660         this.data[name] = value;
22661         if(!this.editing && this.store){
22662             this.store.afterEdit(this);
22663         }       
22664     },
22665
22666     /**
22667      * Get the value of the named field.
22668      * @param {String} name The name of the field to get the value of.
22669      * @return {Object} The value of the field.
22670      */
22671     get : function(name){
22672         return this.data[name]; 
22673     },
22674
22675     // private
22676     beginEdit : function(){
22677         this.editing = true;
22678         this.modified = {}; 
22679     },
22680
22681     // private
22682     cancelEdit : function(){
22683         this.editing = false;
22684         delete this.modified;
22685     },
22686
22687     // private
22688     endEdit : function(){
22689         this.editing = false;
22690         if(this.dirty && this.store){
22691             this.store.afterEdit(this);
22692         }
22693     },
22694
22695     /**
22696      * Usually called by the {@link Roo.data.Store} which owns the Record.
22697      * Rejects all changes made to the Record since either creation, or the last commit operation.
22698      * Modified fields are reverted to their original values.
22699      * <p>
22700      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22701      * of reject operations.
22702      */
22703     reject : function(){
22704         var m = this.modified;
22705         for(var n in m){
22706             if(typeof m[n] != "function"){
22707                 this.data[n] = m[n];
22708             }
22709         }
22710         this.dirty = false;
22711         delete this.modified;
22712         this.editing = false;
22713         if(this.store){
22714             this.store.afterReject(this);
22715         }
22716     },
22717
22718     /**
22719      * Usually called by the {@link Roo.data.Store} which owns the Record.
22720      * Commits all changes made to the Record since either creation, or the last commit operation.
22721      * <p>
22722      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22723      * of commit operations.
22724      */
22725     commit : function(){
22726         this.dirty = false;
22727         delete this.modified;
22728         this.editing = false;
22729         if(this.store){
22730             this.store.afterCommit(this);
22731         }
22732     },
22733
22734     // private
22735     hasError : function(){
22736         return this.error != null;
22737     },
22738
22739     // private
22740     clearError : function(){
22741         this.error = null;
22742     },
22743
22744     /**
22745      * Creates a copy of this record.
22746      * @param {String} id (optional) A new record id if you don't want to use this record's id
22747      * @return {Record}
22748      */
22749     copy : function(newId) {
22750         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
22751     }
22752 };/*
22753  * Based on:
22754  * Ext JS Library 1.1.1
22755  * Copyright(c) 2006-2007, Ext JS, LLC.
22756  *
22757  * Originally Released Under LGPL - original licence link has changed is not relivant.
22758  *
22759  * Fork - LGPL
22760  * <script type="text/javascript">
22761  */
22762
22763
22764
22765 /**
22766  * @class Roo.data.Store
22767  * @extends Roo.util.Observable
22768  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
22769  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
22770  * <p>
22771  * 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
22772  * has no knowledge of the format of the data returned by the Proxy.<br>
22773  * <p>
22774  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
22775  * instances from the data object. These records are cached and made available through accessor functions.
22776  * @constructor
22777  * Creates a new Store.
22778  * @param {Object} config A config object containing the objects needed for the Store to access data,
22779  * and read the data into Records.
22780  */
22781 Roo.data.Store = function(config){
22782     this.data = new Roo.util.MixedCollection(false);
22783     this.data.getKey = function(o){
22784         return o.id;
22785     };
22786     this.baseParams = {};
22787     // private
22788     this.paramNames = {
22789         "start" : "start",
22790         "limit" : "limit",
22791         "sort" : "sort",
22792         "dir" : "dir",
22793         "multisort" : "_multisort"
22794     };
22795
22796     if(config && config.data){
22797         this.inlineData = config.data;
22798         delete config.data;
22799     }
22800
22801     Roo.apply(this, config);
22802     
22803     if(this.reader){ // reader passed
22804         this.reader = Roo.factory(this.reader, Roo.data);
22805         this.reader.xmodule = this.xmodule || false;
22806         if(!this.recordType){
22807             this.recordType = this.reader.recordType;
22808         }
22809         if(this.reader.onMetaChange){
22810             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
22811         }
22812     }
22813
22814     if(this.recordType){
22815         this.fields = this.recordType.prototype.fields;
22816     }
22817     this.modified = [];
22818
22819     this.addEvents({
22820         /**
22821          * @event datachanged
22822          * Fires when the data cache has changed, and a widget which is using this Store
22823          * as a Record cache should refresh its view.
22824          * @param {Store} this
22825          */
22826         datachanged : true,
22827         /**
22828          * @event metachange
22829          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
22830          * @param {Store} this
22831          * @param {Object} meta The JSON metadata
22832          */
22833         metachange : true,
22834         /**
22835          * @event add
22836          * Fires when Records have been added to the Store
22837          * @param {Store} this
22838          * @param {Roo.data.Record[]} records The array of Records added
22839          * @param {Number} index The index at which the record(s) were added
22840          */
22841         add : true,
22842         /**
22843          * @event remove
22844          * Fires when a Record has been removed from the Store
22845          * @param {Store} this
22846          * @param {Roo.data.Record} record The Record that was removed
22847          * @param {Number} index The index at which the record was removed
22848          */
22849         remove : true,
22850         /**
22851          * @event update
22852          * Fires when a Record has been updated
22853          * @param {Store} this
22854          * @param {Roo.data.Record} record The Record that was updated
22855          * @param {String} operation The update operation being performed.  Value may be one of:
22856          * <pre><code>
22857  Roo.data.Record.EDIT
22858  Roo.data.Record.REJECT
22859  Roo.data.Record.COMMIT
22860          * </code></pre>
22861          */
22862         update : true,
22863         /**
22864          * @event clear
22865          * Fires when the data cache has been cleared.
22866          * @param {Store} this
22867          */
22868         clear : true,
22869         /**
22870          * @event beforeload
22871          * Fires before a request is made for a new data object.  If the beforeload handler returns false
22872          * the load action will be canceled.
22873          * @param {Store} this
22874          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22875          */
22876         beforeload : true,
22877         /**
22878          * @event beforeloadadd
22879          * Fires after a new set of Records has been loaded.
22880          * @param {Store} this
22881          * @param {Roo.data.Record[]} records The Records that were loaded
22882          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22883          */
22884         beforeloadadd : true,
22885         /**
22886          * @event load
22887          * Fires after a new set of Records has been loaded, before they are added to the store.
22888          * @param {Store} this
22889          * @param {Roo.data.Record[]} records The Records that were loaded
22890          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22891          * @params {Object} return from reader
22892          */
22893         load : true,
22894         /**
22895          * @event loadexception
22896          * Fires if an exception occurs in the Proxy during loading.
22897          * Called with the signature of the Proxy's "loadexception" event.
22898          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
22899          * 
22900          * @param {Proxy} 
22901          * @param {Object} return from JsonData.reader() - success, totalRecords, records
22902          * @param {Object} load options 
22903          * @param {Object} jsonData from your request (normally this contains the Exception)
22904          */
22905         loadexception : true
22906     });
22907     
22908     if(this.proxy){
22909         this.proxy = Roo.factory(this.proxy, Roo.data);
22910         this.proxy.xmodule = this.xmodule || false;
22911         this.relayEvents(this.proxy,  ["loadexception"]);
22912     }
22913     this.sortToggle = {};
22914     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
22915
22916     Roo.data.Store.superclass.constructor.call(this);
22917
22918     if(this.inlineData){
22919         this.loadData(this.inlineData);
22920         delete this.inlineData;
22921     }
22922 };
22923
22924 Roo.extend(Roo.data.Store, Roo.util.Observable, {
22925      /**
22926     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
22927     * without a remote query - used by combo/forms at present.
22928     */
22929     
22930     /**
22931     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
22932     */
22933     /**
22934     * @cfg {Array} data Inline data to be loaded when the store is initialized.
22935     */
22936     /**
22937     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
22938     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
22939     */
22940     /**
22941     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
22942     * on any HTTP request
22943     */
22944     /**
22945     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
22946     */
22947     /**
22948     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
22949     */
22950     multiSort: false,
22951     /**
22952     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
22953     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
22954     */
22955     remoteSort : false,
22956
22957     /**
22958     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
22959      * loaded or when a record is removed. (defaults to false).
22960     */
22961     pruneModifiedRecords : false,
22962
22963     // private
22964     lastOptions : null,
22965
22966     /**
22967      * Add Records to the Store and fires the add event.
22968      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22969      */
22970     add : function(records){
22971         records = [].concat(records);
22972         for(var i = 0, len = records.length; i < len; i++){
22973             records[i].join(this);
22974         }
22975         var index = this.data.length;
22976         this.data.addAll(records);
22977         this.fireEvent("add", this, records, index);
22978     },
22979
22980     /**
22981      * Remove a Record from the Store and fires the remove event.
22982      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
22983      */
22984     remove : function(record){
22985         var index = this.data.indexOf(record);
22986         this.data.removeAt(index);
22987  
22988         if(this.pruneModifiedRecords){
22989             this.modified.remove(record);
22990         }
22991         this.fireEvent("remove", this, record, index);
22992     },
22993
22994     /**
22995      * Remove all Records from the Store and fires the clear event.
22996      */
22997     removeAll : function(){
22998         this.data.clear();
22999         if(this.pruneModifiedRecords){
23000             this.modified = [];
23001         }
23002         this.fireEvent("clear", this);
23003     },
23004
23005     /**
23006      * Inserts Records to the Store at the given index and fires the add event.
23007      * @param {Number} index The start index at which to insert the passed Records.
23008      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23009      */
23010     insert : function(index, records){
23011         records = [].concat(records);
23012         for(var i = 0, len = records.length; i < len; i++){
23013             this.data.insert(index, records[i]);
23014             records[i].join(this);
23015         }
23016         this.fireEvent("add", this, records, index);
23017     },
23018
23019     /**
23020      * Get the index within the cache of the passed Record.
23021      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
23022      * @return {Number} The index of the passed Record. Returns -1 if not found.
23023      */
23024     indexOf : function(record){
23025         return this.data.indexOf(record);
23026     },
23027
23028     /**
23029      * Get the index within the cache of the Record with the passed id.
23030      * @param {String} id The id of the Record to find.
23031      * @return {Number} The index of the Record. Returns -1 if not found.
23032      */
23033     indexOfId : function(id){
23034         return this.data.indexOfKey(id);
23035     },
23036
23037     /**
23038      * Get the Record with the specified id.
23039      * @param {String} id The id of the Record to find.
23040      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
23041      */
23042     getById : function(id){
23043         return this.data.key(id);
23044     },
23045
23046     /**
23047      * Get the Record at the specified index.
23048      * @param {Number} index The index of the Record to find.
23049      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
23050      */
23051     getAt : function(index){
23052         return this.data.itemAt(index);
23053     },
23054
23055     /**
23056      * Returns a range of Records between specified indices.
23057      * @param {Number} startIndex (optional) The starting index (defaults to 0)
23058      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
23059      * @return {Roo.data.Record[]} An array of Records
23060      */
23061     getRange : function(start, end){
23062         return this.data.getRange(start, end);
23063     },
23064
23065     // private
23066     storeOptions : function(o){
23067         o = Roo.apply({}, o);
23068         delete o.callback;
23069         delete o.scope;
23070         this.lastOptions = o;
23071     },
23072
23073     /**
23074      * Loads the Record cache from the configured Proxy using the configured Reader.
23075      * <p>
23076      * If using remote paging, then the first load call must specify the <em>start</em>
23077      * and <em>limit</em> properties in the options.params property to establish the initial
23078      * position within the dataset, and the number of Records to cache on each read from the Proxy.
23079      * <p>
23080      * <strong>It is important to note that for remote data sources, loading is asynchronous,
23081      * and this call will return before the new data has been loaded. Perform any post-processing
23082      * in a callback function, or in a "load" event handler.</strong>
23083      * <p>
23084      * @param {Object} options An object containing properties which control loading options:<ul>
23085      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23086      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23087      * passed the following arguments:<ul>
23088      * <li>r : Roo.data.Record[]</li>
23089      * <li>options: Options object from the load call</li>
23090      * <li>success: Boolean success indicator</li></ul></li>
23091      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23092      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23093      * </ul>
23094      */
23095     load : function(options){
23096         options = options || {};
23097         if(this.fireEvent("beforeload", this, options) !== false){
23098             this.storeOptions(options);
23099             var p = Roo.apply(options.params || {}, this.baseParams);
23100             // if meta was not loaded from remote source.. try requesting it.
23101             if (!this.reader.metaFromRemote) {
23102                 p._requestMeta = 1;
23103             }
23104             if(this.sortInfo && this.remoteSort){
23105                 var pn = this.paramNames;
23106                 p[pn["sort"]] = this.sortInfo.field;
23107                 p[pn["dir"]] = this.sortInfo.direction;
23108             }
23109             if (this.multiSort) {
23110                 var pn = this.paramNames;
23111                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23112             }
23113             
23114             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23115         }
23116     },
23117
23118     /**
23119      * Reloads the Record cache from the configured Proxy using the configured Reader and
23120      * the options from the last load operation performed.
23121      * @param {Object} options (optional) An object containing properties which may override the options
23122      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23123      * the most recently used options are reused).
23124      */
23125     reload : function(options){
23126         this.load(Roo.applyIf(options||{}, this.lastOptions));
23127     },
23128
23129     // private
23130     // Called as a callback by the Reader during a load operation.
23131     loadRecords : function(o, options, success){
23132         if(!o || success === false){
23133             if(success !== false){
23134                 this.fireEvent("load", this, [], options, o);
23135             }
23136             if(options.callback){
23137                 options.callback.call(options.scope || this, [], options, false);
23138             }
23139             return;
23140         }
23141         // if data returned failure - throw an exception.
23142         if (o.success === false) {
23143             // show a message if no listener is registered.
23144             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23145                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23146             }
23147             // loadmask wil be hooked into this..
23148             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23149             return;
23150         }
23151         var r = o.records, t = o.totalRecords || r.length;
23152         
23153         this.fireEvent("beforeloadadd", this, r, options, o);
23154         
23155         if(!options || options.add !== true){
23156             if(this.pruneModifiedRecords){
23157                 this.modified = [];
23158             }
23159             for(var i = 0, len = r.length; i < len; i++){
23160                 r[i].join(this);
23161             }
23162             if(this.snapshot){
23163                 this.data = this.snapshot;
23164                 delete this.snapshot;
23165             }
23166             this.data.clear();
23167             this.data.addAll(r);
23168             this.totalLength = t;
23169             this.applySort();
23170             this.fireEvent("datachanged", this);
23171         }else{
23172             this.totalLength = Math.max(t, this.data.length+r.length);
23173             this.add(r);
23174         }
23175         
23176         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23177                 
23178             var e = new Roo.data.Record({});
23179
23180             e.set(this.parent.displayField, this.parent.emptyTitle);
23181             e.set(this.parent.valueField, '');
23182
23183             this.insert(0, e);
23184         }
23185             
23186         this.fireEvent("load", this, r, options, o);
23187         if(options.callback){
23188             options.callback.call(options.scope || this, r, options, true);
23189         }
23190     },
23191
23192
23193     /**
23194      * Loads data from a passed data block. A Reader which understands the format of the data
23195      * must have been configured in the constructor.
23196      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23197      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23198      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23199      */
23200     loadData : function(o, append){
23201         var r = this.reader.readRecords(o);
23202         this.loadRecords(r, {add: append}, true);
23203     },
23204
23205     /**
23206      * Gets the number of cached records.
23207      * <p>
23208      * <em>If using paging, this may not be the total size of the dataset. If the data object
23209      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23210      * the data set size</em>
23211      */
23212     getCount : function(){
23213         return this.data.length || 0;
23214     },
23215
23216     /**
23217      * Gets the total number of records in the dataset as returned by the server.
23218      * <p>
23219      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23220      * the dataset size</em>
23221      */
23222     getTotalCount : function(){
23223         return this.totalLength || 0;
23224     },
23225
23226     /**
23227      * Returns the sort state of the Store as an object with two properties:
23228      * <pre><code>
23229  field {String} The name of the field by which the Records are sorted
23230  direction {String} The sort order, "ASC" or "DESC"
23231      * </code></pre>
23232      */
23233     getSortState : function(){
23234         return this.sortInfo;
23235     },
23236
23237     // private
23238     applySort : function(){
23239         if(this.sortInfo && !this.remoteSort){
23240             var s = this.sortInfo, f = s.field;
23241             var st = this.fields.get(f).sortType;
23242             var fn = function(r1, r2){
23243                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23244                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23245             };
23246             this.data.sort(s.direction, fn);
23247             if(this.snapshot && this.snapshot != this.data){
23248                 this.snapshot.sort(s.direction, fn);
23249             }
23250         }
23251     },
23252
23253     /**
23254      * Sets the default sort column and order to be used by the next load operation.
23255      * @param {String} fieldName The name of the field to sort by.
23256      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23257      */
23258     setDefaultSort : function(field, dir){
23259         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23260     },
23261
23262     /**
23263      * Sort the Records.
23264      * If remote sorting is used, the sort is performed on the server, and the cache is
23265      * reloaded. If local sorting is used, the cache is sorted internally.
23266      * @param {String} fieldName The name of the field to sort by.
23267      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23268      */
23269     sort : function(fieldName, dir){
23270         var f = this.fields.get(fieldName);
23271         if(!dir){
23272             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23273             
23274             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23275                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23276             }else{
23277                 dir = f.sortDir;
23278             }
23279         }
23280         this.sortToggle[f.name] = dir;
23281         this.sortInfo = {field: f.name, direction: dir};
23282         if(!this.remoteSort){
23283             this.applySort();
23284             this.fireEvent("datachanged", this);
23285         }else{
23286             this.load(this.lastOptions);
23287         }
23288     },
23289
23290     /**
23291      * Calls the specified function for each of the Records in the cache.
23292      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23293      * Returning <em>false</em> aborts and exits the iteration.
23294      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23295      */
23296     each : function(fn, scope){
23297         this.data.each(fn, scope);
23298     },
23299
23300     /**
23301      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23302      * (e.g., during paging).
23303      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23304      */
23305     getModifiedRecords : function(){
23306         return this.modified;
23307     },
23308
23309     // private
23310     createFilterFn : function(property, value, anyMatch){
23311         if(!value.exec){ // not a regex
23312             value = String(value);
23313             if(value.length == 0){
23314                 return false;
23315             }
23316             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23317         }
23318         return function(r){
23319             return value.test(r.data[property]);
23320         };
23321     },
23322
23323     /**
23324      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23325      * @param {String} property A field on your records
23326      * @param {Number} start The record index to start at (defaults to 0)
23327      * @param {Number} end The last record index to include (defaults to length - 1)
23328      * @return {Number} The sum
23329      */
23330     sum : function(property, start, end){
23331         var rs = this.data.items, v = 0;
23332         start = start || 0;
23333         end = (end || end === 0) ? end : rs.length-1;
23334
23335         for(var i = start; i <= end; i++){
23336             v += (rs[i].data[property] || 0);
23337         }
23338         return v;
23339     },
23340
23341     /**
23342      * Filter the records by a specified property.
23343      * @param {String} field A field on your records
23344      * @param {String/RegExp} value Either a string that the field
23345      * should start with or a RegExp to test against the field
23346      * @param {Boolean} anyMatch True to match any part not just the beginning
23347      */
23348     filter : function(property, value, anyMatch){
23349         var fn = this.createFilterFn(property, value, anyMatch);
23350         return fn ? this.filterBy(fn) : this.clearFilter();
23351     },
23352
23353     /**
23354      * Filter by a function. The specified function will be called with each
23355      * record in this data source. If the function returns true the record is included,
23356      * otherwise it is filtered.
23357      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23358      * @param {Object} scope (optional) The scope of the function (defaults to this)
23359      */
23360     filterBy : function(fn, scope){
23361         this.snapshot = this.snapshot || this.data;
23362         this.data = this.queryBy(fn, scope||this);
23363         this.fireEvent("datachanged", this);
23364     },
23365
23366     /**
23367      * Query the records by a specified property.
23368      * @param {String} field A field on your records
23369      * @param {String/RegExp} value Either a string that the field
23370      * should start with or a RegExp to test against the field
23371      * @param {Boolean} anyMatch True to match any part not just the beginning
23372      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23373      */
23374     query : function(property, value, anyMatch){
23375         var fn = this.createFilterFn(property, value, anyMatch);
23376         return fn ? this.queryBy(fn) : this.data.clone();
23377     },
23378
23379     /**
23380      * Query by a function. The specified function will be called with each
23381      * record in this data source. If the function returns true the record is included
23382      * in the results.
23383      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23384      * @param {Object} scope (optional) The scope of the function (defaults to this)
23385       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23386      **/
23387     queryBy : function(fn, scope){
23388         var data = this.snapshot || this.data;
23389         return data.filterBy(fn, scope||this);
23390     },
23391
23392     /**
23393      * Collects unique values for a particular dataIndex from this store.
23394      * @param {String} dataIndex The property to collect
23395      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23396      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23397      * @return {Array} An array of the unique values
23398      **/
23399     collect : function(dataIndex, allowNull, bypassFilter){
23400         var d = (bypassFilter === true && this.snapshot) ?
23401                 this.snapshot.items : this.data.items;
23402         var v, sv, r = [], l = {};
23403         for(var i = 0, len = d.length; i < len; i++){
23404             v = d[i].data[dataIndex];
23405             sv = String(v);
23406             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23407                 l[sv] = true;
23408                 r[r.length] = v;
23409             }
23410         }
23411         return r;
23412     },
23413
23414     /**
23415      * Revert to a view of the Record cache with no filtering applied.
23416      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
23417      */
23418     clearFilter : function(suppressEvent){
23419         if(this.snapshot && this.snapshot != this.data){
23420             this.data = this.snapshot;
23421             delete this.snapshot;
23422             if(suppressEvent !== true){
23423                 this.fireEvent("datachanged", this);
23424             }
23425         }
23426     },
23427
23428     // private
23429     afterEdit : function(record){
23430         if(this.modified.indexOf(record) == -1){
23431             this.modified.push(record);
23432         }
23433         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
23434     },
23435     
23436     // private
23437     afterReject : function(record){
23438         this.modified.remove(record);
23439         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
23440     },
23441
23442     // private
23443     afterCommit : function(record){
23444         this.modified.remove(record);
23445         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
23446     },
23447
23448     /**
23449      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
23450      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
23451      */
23452     commitChanges : function(){
23453         var m = this.modified.slice(0);
23454         this.modified = [];
23455         for(var i = 0, len = m.length; i < len; i++){
23456             m[i].commit();
23457         }
23458     },
23459
23460     /**
23461      * Cancel outstanding changes on all changed records.
23462      */
23463     rejectChanges : function(){
23464         var m = this.modified.slice(0);
23465         this.modified = [];
23466         for(var i = 0, len = m.length; i < len; i++){
23467             m[i].reject();
23468         }
23469     },
23470
23471     onMetaChange : function(meta, rtype, o){
23472         this.recordType = rtype;
23473         this.fields = rtype.prototype.fields;
23474         delete this.snapshot;
23475         this.sortInfo = meta.sortInfo || this.sortInfo;
23476         this.modified = [];
23477         this.fireEvent('metachange', this, this.reader.meta);
23478     },
23479     
23480     moveIndex : function(data, type)
23481     {
23482         var index = this.indexOf(data);
23483         
23484         var newIndex = index + type;
23485         
23486         this.remove(data);
23487         
23488         this.insert(newIndex, data);
23489         
23490     }
23491 });/*
23492  * Based on:
23493  * Ext JS Library 1.1.1
23494  * Copyright(c) 2006-2007, Ext JS, LLC.
23495  *
23496  * Originally Released Under LGPL - original licence link has changed is not relivant.
23497  *
23498  * Fork - LGPL
23499  * <script type="text/javascript">
23500  */
23501
23502 /**
23503  * @class Roo.data.SimpleStore
23504  * @extends Roo.data.Store
23505  * Small helper class to make creating Stores from Array data easier.
23506  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
23507  * @cfg {Array} fields An array of field definition objects, or field name strings.
23508  * @cfg {Array} data The multi-dimensional array of data
23509  * @constructor
23510  * @param {Object} config
23511  */
23512 Roo.data.SimpleStore = function(config){
23513     Roo.data.SimpleStore.superclass.constructor.call(this, {
23514         isLocal : true,
23515         reader: new Roo.data.ArrayReader({
23516                 id: config.id
23517             },
23518             Roo.data.Record.create(config.fields)
23519         ),
23520         proxy : new Roo.data.MemoryProxy(config.data)
23521     });
23522     this.load();
23523 };
23524 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
23525  * Based on:
23526  * Ext JS Library 1.1.1
23527  * Copyright(c) 2006-2007, Ext JS, LLC.
23528  *
23529  * Originally Released Under LGPL - original licence link has changed is not relivant.
23530  *
23531  * Fork - LGPL
23532  * <script type="text/javascript">
23533  */
23534
23535 /**
23536 /**
23537  * @extends Roo.data.Store
23538  * @class Roo.data.JsonStore
23539  * Small helper class to make creating Stores for JSON data easier. <br/>
23540 <pre><code>
23541 var store = new Roo.data.JsonStore({
23542     url: 'get-images.php',
23543     root: 'images',
23544     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
23545 });
23546 </code></pre>
23547  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
23548  * JsonReader and HttpProxy (unless inline data is provided).</b>
23549  * @cfg {Array} fields An array of field definition objects, or field name strings.
23550  * @constructor
23551  * @param {Object} config
23552  */
23553 Roo.data.JsonStore = function(c){
23554     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
23555         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
23556         reader: new Roo.data.JsonReader(c, c.fields)
23557     }));
23558 };
23559 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
23560  * Based on:
23561  * Ext JS Library 1.1.1
23562  * Copyright(c) 2006-2007, Ext JS, LLC.
23563  *
23564  * Originally Released Under LGPL - original licence link has changed is not relivant.
23565  *
23566  * Fork - LGPL
23567  * <script type="text/javascript">
23568  */
23569
23570  
23571 Roo.data.Field = function(config){
23572     if(typeof config == "string"){
23573         config = {name: config};
23574     }
23575     Roo.apply(this, config);
23576     
23577     if(!this.type){
23578         this.type = "auto";
23579     }
23580     
23581     var st = Roo.data.SortTypes;
23582     // named sortTypes are supported, here we look them up
23583     if(typeof this.sortType == "string"){
23584         this.sortType = st[this.sortType];
23585     }
23586     
23587     // set default sortType for strings and dates
23588     if(!this.sortType){
23589         switch(this.type){
23590             case "string":
23591                 this.sortType = st.asUCString;
23592                 break;
23593             case "date":
23594                 this.sortType = st.asDate;
23595                 break;
23596             default:
23597                 this.sortType = st.none;
23598         }
23599     }
23600
23601     // define once
23602     var stripRe = /[\$,%]/g;
23603
23604     // prebuilt conversion function for this field, instead of
23605     // switching every time we're reading a value
23606     if(!this.convert){
23607         var cv, dateFormat = this.dateFormat;
23608         switch(this.type){
23609             case "":
23610             case "auto":
23611             case undefined:
23612                 cv = function(v){ return v; };
23613                 break;
23614             case "string":
23615                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
23616                 break;
23617             case "int":
23618                 cv = function(v){
23619                     return v !== undefined && v !== null && v !== '' ?
23620                            parseInt(String(v).replace(stripRe, ""), 10) : '';
23621                     };
23622                 break;
23623             case "float":
23624                 cv = function(v){
23625                     return v !== undefined && v !== null && v !== '' ?
23626                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
23627                     };
23628                 break;
23629             case "bool":
23630             case "boolean":
23631                 cv = function(v){ return v === true || v === "true" || v == 1; };
23632                 break;
23633             case "date":
23634                 cv = function(v){
23635                     if(!v){
23636                         return '';
23637                     }
23638                     if(v instanceof Date){
23639                         return v;
23640                     }
23641                     if(dateFormat){
23642                         if(dateFormat == "timestamp"){
23643                             return new Date(v*1000);
23644                         }
23645                         return Date.parseDate(v, dateFormat);
23646                     }
23647                     var parsed = Date.parse(v);
23648                     return parsed ? new Date(parsed) : null;
23649                 };
23650              break;
23651             
23652         }
23653         this.convert = cv;
23654     }
23655 };
23656
23657 Roo.data.Field.prototype = {
23658     dateFormat: null,
23659     defaultValue: "",
23660     mapping: null,
23661     sortType : null,
23662     sortDir : "ASC"
23663 };/*
23664  * Based on:
23665  * Ext JS Library 1.1.1
23666  * Copyright(c) 2006-2007, Ext JS, LLC.
23667  *
23668  * Originally Released Under LGPL - original licence link has changed is not relivant.
23669  *
23670  * Fork - LGPL
23671  * <script type="text/javascript">
23672  */
23673  
23674 // Base class for reading structured data from a data source.  This class is intended to be
23675 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
23676
23677 /**
23678  * @class Roo.data.DataReader
23679  * Base class for reading structured data from a data source.  This class is intended to be
23680  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
23681  */
23682
23683 Roo.data.DataReader = function(meta, recordType){
23684     
23685     this.meta = meta;
23686     
23687     this.recordType = recordType instanceof Array ? 
23688         Roo.data.Record.create(recordType) : recordType;
23689 };
23690
23691 Roo.data.DataReader.prototype = {
23692      /**
23693      * Create an empty record
23694      * @param {Object} data (optional) - overlay some values
23695      * @return {Roo.data.Record} record created.
23696      */
23697     newRow :  function(d) {
23698         var da =  {};
23699         this.recordType.prototype.fields.each(function(c) {
23700             switch( c.type) {
23701                 case 'int' : da[c.name] = 0; break;
23702                 case 'date' : da[c.name] = new Date(); break;
23703                 case 'float' : da[c.name] = 0.0; break;
23704                 case 'boolean' : da[c.name] = false; break;
23705                 default : da[c.name] = ""; break;
23706             }
23707             
23708         });
23709         return new this.recordType(Roo.apply(da, d));
23710     }
23711     
23712 };/*
23713  * Based on:
23714  * Ext JS Library 1.1.1
23715  * Copyright(c) 2006-2007, Ext JS, LLC.
23716  *
23717  * Originally Released Under LGPL - original licence link has changed is not relivant.
23718  *
23719  * Fork - LGPL
23720  * <script type="text/javascript">
23721  */
23722
23723 /**
23724  * @class Roo.data.DataProxy
23725  * @extends Roo.data.Observable
23726  * This class is an abstract base class for implementations which provide retrieval of
23727  * unformatted data objects.<br>
23728  * <p>
23729  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
23730  * (of the appropriate type which knows how to parse the data object) to provide a block of
23731  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
23732  * <p>
23733  * Custom implementations must implement the load method as described in
23734  * {@link Roo.data.HttpProxy#load}.
23735  */
23736 Roo.data.DataProxy = function(){
23737     this.addEvents({
23738         /**
23739          * @event beforeload
23740          * Fires before a network request is made to retrieve a data object.
23741          * @param {Object} This DataProxy object.
23742          * @param {Object} params The params parameter to the load function.
23743          */
23744         beforeload : true,
23745         /**
23746          * @event load
23747          * Fires before the load method's callback is called.
23748          * @param {Object} This DataProxy object.
23749          * @param {Object} o The data object.
23750          * @param {Object} arg The callback argument object passed to the load function.
23751          */
23752         load : true,
23753         /**
23754          * @event loadexception
23755          * Fires if an Exception occurs during data retrieval.
23756          * @param {Object} This DataProxy object.
23757          * @param {Object} o The data object.
23758          * @param {Object} arg The callback argument object passed to the load function.
23759          * @param {Object} e The Exception.
23760          */
23761         loadexception : true
23762     });
23763     Roo.data.DataProxy.superclass.constructor.call(this);
23764 };
23765
23766 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
23767
23768     /**
23769      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
23770      */
23771 /*
23772  * Based on:
23773  * Ext JS Library 1.1.1
23774  * Copyright(c) 2006-2007, Ext JS, LLC.
23775  *
23776  * Originally Released Under LGPL - original licence link has changed is not relivant.
23777  *
23778  * Fork - LGPL
23779  * <script type="text/javascript">
23780  */
23781 /**
23782  * @class Roo.data.MemoryProxy
23783  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
23784  * to the Reader when its load method is called.
23785  * @constructor
23786  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
23787  */
23788 Roo.data.MemoryProxy = function(data){
23789     if (data.data) {
23790         data = data.data;
23791     }
23792     Roo.data.MemoryProxy.superclass.constructor.call(this);
23793     this.data = data;
23794 };
23795
23796 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
23797     
23798     /**
23799      * Load data from the requested source (in this case an in-memory
23800      * data object passed to the constructor), read the data object into
23801      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23802      * process that block using the passed callback.
23803      * @param {Object} params This parameter is not used by the MemoryProxy class.
23804      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23805      * object into a block of Roo.data.Records.
23806      * @param {Function} callback The function into which to pass the block of Roo.data.records.
23807      * The function must be passed <ul>
23808      * <li>The Record block object</li>
23809      * <li>The "arg" argument from the load function</li>
23810      * <li>A boolean success indicator</li>
23811      * </ul>
23812      * @param {Object} scope The scope in which to call the callback
23813      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23814      */
23815     load : function(params, reader, callback, scope, arg){
23816         params = params || {};
23817         var result;
23818         try {
23819             result = reader.readRecords(this.data);
23820         }catch(e){
23821             this.fireEvent("loadexception", this, arg, null, e);
23822             callback.call(scope, null, arg, false);
23823             return;
23824         }
23825         callback.call(scope, result, arg, true);
23826     },
23827     
23828     // private
23829     update : function(params, records){
23830         
23831     }
23832 });/*
23833  * Based on:
23834  * Ext JS Library 1.1.1
23835  * Copyright(c) 2006-2007, Ext JS, LLC.
23836  *
23837  * Originally Released Under LGPL - original licence link has changed is not relivant.
23838  *
23839  * Fork - LGPL
23840  * <script type="text/javascript">
23841  */
23842 /**
23843  * @class Roo.data.HttpProxy
23844  * @extends Roo.data.DataProxy
23845  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
23846  * configured to reference a certain URL.<br><br>
23847  * <p>
23848  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
23849  * from which the running page was served.<br><br>
23850  * <p>
23851  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
23852  * <p>
23853  * Be aware that to enable the browser to parse an XML document, the server must set
23854  * the Content-Type header in the HTTP response to "text/xml".
23855  * @constructor
23856  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
23857  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
23858  * will be used to make the request.
23859  */
23860 Roo.data.HttpProxy = function(conn){
23861     Roo.data.HttpProxy.superclass.constructor.call(this);
23862     // is conn a conn config or a real conn?
23863     this.conn = conn;
23864     this.useAjax = !conn || !conn.events;
23865   
23866 };
23867
23868 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
23869     // thse are take from connection...
23870     
23871     /**
23872      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
23873      */
23874     /**
23875      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
23876      * extra parameters to each request made by this object. (defaults to undefined)
23877      */
23878     /**
23879      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
23880      *  to each request made by this object. (defaults to undefined)
23881      */
23882     /**
23883      * @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)
23884      */
23885     /**
23886      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
23887      */
23888      /**
23889      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
23890      * @type Boolean
23891      */
23892   
23893
23894     /**
23895      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
23896      * @type Boolean
23897      */
23898     /**
23899      * Return the {@link Roo.data.Connection} object being used by this Proxy.
23900      * @return {Connection} The Connection object. This object may be used to subscribe to events on
23901      * a finer-grained basis than the DataProxy events.
23902      */
23903     getConnection : function(){
23904         return this.useAjax ? Roo.Ajax : this.conn;
23905     },
23906
23907     /**
23908      * Load data from the configured {@link Roo.data.Connection}, read the data object into
23909      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
23910      * process that block using the passed callback.
23911      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23912      * for the request to the remote server.
23913      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23914      * object into a block of Roo.data.Records.
23915      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23916      * The function must be passed <ul>
23917      * <li>The Record block object</li>
23918      * <li>The "arg" argument from the load function</li>
23919      * <li>A boolean success indicator</li>
23920      * </ul>
23921      * @param {Object} scope The scope in which to call the callback
23922      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23923      */
23924     load : function(params, reader, callback, scope, arg){
23925         if(this.fireEvent("beforeload", this, params) !== false){
23926             var  o = {
23927                 params : params || {},
23928                 request: {
23929                     callback : callback,
23930                     scope : scope,
23931                     arg : arg
23932                 },
23933                 reader: reader,
23934                 callback : this.loadResponse,
23935                 scope: this
23936             };
23937             if(this.useAjax){
23938                 Roo.applyIf(o, this.conn);
23939                 if(this.activeRequest){
23940                     Roo.Ajax.abort(this.activeRequest);
23941                 }
23942                 this.activeRequest = Roo.Ajax.request(o);
23943             }else{
23944                 this.conn.request(o);
23945             }
23946         }else{
23947             callback.call(scope||this, null, arg, false);
23948         }
23949     },
23950
23951     // private
23952     loadResponse : function(o, success, response){
23953         delete this.activeRequest;
23954         if(!success){
23955             this.fireEvent("loadexception", this, o, response);
23956             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23957             return;
23958         }
23959         var result;
23960         try {
23961             result = o.reader.read(response);
23962         }catch(e){
23963             this.fireEvent("loadexception", this, o, response, e);
23964             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23965             return;
23966         }
23967         
23968         this.fireEvent("load", this, o, o.request.arg);
23969         o.request.callback.call(o.request.scope, result, o.request.arg, true);
23970     },
23971
23972     // private
23973     update : function(dataSet){
23974
23975     },
23976
23977     // private
23978     updateResponse : function(dataSet){
23979
23980     }
23981 });/*
23982  * Based on:
23983  * Ext JS Library 1.1.1
23984  * Copyright(c) 2006-2007, Ext JS, LLC.
23985  *
23986  * Originally Released Under LGPL - original licence link has changed is not relivant.
23987  *
23988  * Fork - LGPL
23989  * <script type="text/javascript">
23990  */
23991
23992 /**
23993  * @class Roo.data.ScriptTagProxy
23994  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
23995  * other than the originating domain of the running page.<br><br>
23996  * <p>
23997  * <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
23998  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
23999  * <p>
24000  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
24001  * source code that is used as the source inside a &lt;script> tag.<br><br>
24002  * <p>
24003  * In order for the browser to process the returned data, the server must wrap the data object
24004  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
24005  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
24006  * depending on whether the callback name was passed:
24007  * <p>
24008  * <pre><code>
24009 boolean scriptTag = false;
24010 String cb = request.getParameter("callback");
24011 if (cb != null) {
24012     scriptTag = true;
24013     response.setContentType("text/javascript");
24014 } else {
24015     response.setContentType("application/x-json");
24016 }
24017 Writer out = response.getWriter();
24018 if (scriptTag) {
24019     out.write(cb + "(");
24020 }
24021 out.print(dataBlock.toJsonString());
24022 if (scriptTag) {
24023     out.write(");");
24024 }
24025 </pre></code>
24026  *
24027  * @constructor
24028  * @param {Object} config A configuration object.
24029  */
24030 Roo.data.ScriptTagProxy = function(config){
24031     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
24032     Roo.apply(this, config);
24033     this.head = document.getElementsByTagName("head")[0];
24034 };
24035
24036 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
24037
24038 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
24039     /**
24040      * @cfg {String} url The URL from which to request the data object.
24041      */
24042     /**
24043      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
24044      */
24045     timeout : 30000,
24046     /**
24047      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
24048      * the server the name of the callback function set up by the load call to process the returned data object.
24049      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
24050      * javascript output which calls this named function passing the data object as its only parameter.
24051      */
24052     callbackParam : "callback",
24053     /**
24054      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
24055      * name to the request.
24056      */
24057     nocache : true,
24058
24059     /**
24060      * Load data from the configured URL, read the data object into
24061      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24062      * process that block using the passed callback.
24063      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24064      * for the request to the remote server.
24065      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24066      * object into a block of Roo.data.Records.
24067      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24068      * The function must be passed <ul>
24069      * <li>The Record block object</li>
24070      * <li>The "arg" argument from the load function</li>
24071      * <li>A boolean success indicator</li>
24072      * </ul>
24073      * @param {Object} scope The scope in which to call the callback
24074      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24075      */
24076     load : function(params, reader, callback, scope, arg){
24077         if(this.fireEvent("beforeload", this, params) !== false){
24078
24079             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
24080
24081             var url = this.url;
24082             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24083             if(this.nocache){
24084                 url += "&_dc=" + (new Date().getTime());
24085             }
24086             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24087             var trans = {
24088                 id : transId,
24089                 cb : "stcCallback"+transId,
24090                 scriptId : "stcScript"+transId,
24091                 params : params,
24092                 arg : arg,
24093                 url : url,
24094                 callback : callback,
24095                 scope : scope,
24096                 reader : reader
24097             };
24098             var conn = this;
24099
24100             window[trans.cb] = function(o){
24101                 conn.handleResponse(o, trans);
24102             };
24103
24104             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24105
24106             if(this.autoAbort !== false){
24107                 this.abort();
24108             }
24109
24110             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24111
24112             var script = document.createElement("script");
24113             script.setAttribute("src", url);
24114             script.setAttribute("type", "text/javascript");
24115             script.setAttribute("id", trans.scriptId);
24116             this.head.appendChild(script);
24117
24118             this.trans = trans;
24119         }else{
24120             callback.call(scope||this, null, arg, false);
24121         }
24122     },
24123
24124     // private
24125     isLoading : function(){
24126         return this.trans ? true : false;
24127     },
24128
24129     /**
24130      * Abort the current server request.
24131      */
24132     abort : function(){
24133         if(this.isLoading()){
24134             this.destroyTrans(this.trans);
24135         }
24136     },
24137
24138     // private
24139     destroyTrans : function(trans, isLoaded){
24140         this.head.removeChild(document.getElementById(trans.scriptId));
24141         clearTimeout(trans.timeoutId);
24142         if(isLoaded){
24143             window[trans.cb] = undefined;
24144             try{
24145                 delete window[trans.cb];
24146             }catch(e){}
24147         }else{
24148             // if hasn't been loaded, wait for load to remove it to prevent script error
24149             window[trans.cb] = function(){
24150                 window[trans.cb] = undefined;
24151                 try{
24152                     delete window[trans.cb];
24153                 }catch(e){}
24154             };
24155         }
24156     },
24157
24158     // private
24159     handleResponse : function(o, trans){
24160         this.trans = false;
24161         this.destroyTrans(trans, true);
24162         var result;
24163         try {
24164             result = trans.reader.readRecords(o);
24165         }catch(e){
24166             this.fireEvent("loadexception", this, o, trans.arg, e);
24167             trans.callback.call(trans.scope||window, null, trans.arg, false);
24168             return;
24169         }
24170         this.fireEvent("load", this, o, trans.arg);
24171         trans.callback.call(trans.scope||window, result, trans.arg, true);
24172     },
24173
24174     // private
24175     handleFailure : function(trans){
24176         this.trans = false;
24177         this.destroyTrans(trans, false);
24178         this.fireEvent("loadexception", this, null, trans.arg);
24179         trans.callback.call(trans.scope||window, null, trans.arg, false);
24180     }
24181 });/*
24182  * Based on:
24183  * Ext JS Library 1.1.1
24184  * Copyright(c) 2006-2007, Ext JS, LLC.
24185  *
24186  * Originally Released Under LGPL - original licence link has changed is not relivant.
24187  *
24188  * Fork - LGPL
24189  * <script type="text/javascript">
24190  */
24191
24192 /**
24193  * @class Roo.data.JsonReader
24194  * @extends Roo.data.DataReader
24195  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24196  * based on mappings in a provided Roo.data.Record constructor.
24197  * 
24198  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24199  * in the reply previously. 
24200  * 
24201  * <p>
24202  * Example code:
24203  * <pre><code>
24204 var RecordDef = Roo.data.Record.create([
24205     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24206     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24207 ]);
24208 var myReader = new Roo.data.JsonReader({
24209     totalProperty: "results",    // The property which contains the total dataset size (optional)
24210     root: "rows",                // The property which contains an Array of row objects
24211     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24212 }, RecordDef);
24213 </code></pre>
24214  * <p>
24215  * This would consume a JSON file like this:
24216  * <pre><code>
24217 { 'results': 2, 'rows': [
24218     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24219     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24220 }
24221 </code></pre>
24222  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24223  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24224  * paged from the remote server.
24225  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24226  * @cfg {String} root name of the property which contains the Array of row objects.
24227  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24228  * @cfg {Array} fields Array of field definition objects
24229  * @constructor
24230  * Create a new JsonReader
24231  * @param {Object} meta Metadata configuration options
24232  * @param {Object} recordType Either an Array of field definition objects,
24233  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24234  */
24235 Roo.data.JsonReader = function(meta, recordType){
24236     
24237     meta = meta || {};
24238     // set some defaults:
24239     Roo.applyIf(meta, {
24240         totalProperty: 'total',
24241         successProperty : 'success',
24242         root : 'data',
24243         id : 'id'
24244     });
24245     
24246     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24247 };
24248 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24249     
24250     /**
24251      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24252      * Used by Store query builder to append _requestMeta to params.
24253      * 
24254      */
24255     metaFromRemote : false,
24256     /**
24257      * This method is only used by a DataProxy which has retrieved data from a remote server.
24258      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24259      * @return {Object} data A data block which is used by an Roo.data.Store object as
24260      * a cache of Roo.data.Records.
24261      */
24262     read : function(response){
24263         var json = response.responseText;
24264        
24265         var o = /* eval:var:o */ eval("("+json+")");
24266         if(!o) {
24267             throw {message: "JsonReader.read: Json object not found"};
24268         }
24269         
24270         if(o.metaData){
24271             
24272             delete this.ef;
24273             this.metaFromRemote = true;
24274             this.meta = o.metaData;
24275             this.recordType = Roo.data.Record.create(o.metaData.fields);
24276             this.onMetaChange(this.meta, this.recordType, o);
24277         }
24278         return this.readRecords(o);
24279     },
24280
24281     // private function a store will implement
24282     onMetaChange : function(meta, recordType, o){
24283
24284     },
24285
24286     /**
24287          * @ignore
24288          */
24289     simpleAccess: function(obj, subsc) {
24290         return obj[subsc];
24291     },
24292
24293         /**
24294          * @ignore
24295          */
24296     getJsonAccessor: function(){
24297         var re = /[\[\.]/;
24298         return function(expr) {
24299             try {
24300                 return(re.test(expr))
24301                     ? new Function("obj", "return obj." + expr)
24302                     : function(obj){
24303                         return obj[expr];
24304                     };
24305             } catch(e){}
24306             return Roo.emptyFn;
24307         };
24308     }(),
24309
24310     /**
24311      * Create a data block containing Roo.data.Records from an XML document.
24312      * @param {Object} o An object which contains an Array of row objects in the property specified
24313      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24314      * which contains the total size of the dataset.
24315      * @return {Object} data A data block which is used by an Roo.data.Store object as
24316      * a cache of Roo.data.Records.
24317      */
24318     readRecords : function(o){
24319         /**
24320          * After any data loads, the raw JSON data is available for further custom processing.
24321          * @type Object
24322          */
24323         this.o = o;
24324         var s = this.meta, Record = this.recordType,
24325             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24326
24327 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24328         if (!this.ef) {
24329             if(s.totalProperty) {
24330                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24331                 }
24332                 if(s.successProperty) {
24333                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24334                 }
24335                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24336                 if (s.id) {
24337                         var g = this.getJsonAccessor(s.id);
24338                         this.getId = function(rec) {
24339                                 var r = g(rec);  
24340                                 return (r === undefined || r === "") ? null : r;
24341                         };
24342                 } else {
24343                         this.getId = function(){return null;};
24344                 }
24345             this.ef = [];
24346             for(var jj = 0; jj < fl; jj++){
24347                 f = fi[jj];
24348                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24349                 this.ef[jj] = this.getJsonAccessor(map);
24350             }
24351         }
24352
24353         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24354         if(s.totalProperty){
24355             var vt = parseInt(this.getTotal(o), 10);
24356             if(!isNaN(vt)){
24357                 totalRecords = vt;
24358             }
24359         }
24360         if(s.successProperty){
24361             var vs = this.getSuccess(o);
24362             if(vs === false || vs === 'false'){
24363                 success = false;
24364             }
24365         }
24366         var records = [];
24367         for(var i = 0; i < c; i++){
24368                 var n = root[i];
24369             var values = {};
24370             var id = this.getId(n);
24371             for(var j = 0; j < fl; j++){
24372                 f = fi[j];
24373             var v = this.ef[j](n);
24374             if (!f.convert) {
24375                 Roo.log('missing convert for ' + f.name);
24376                 Roo.log(f);
24377                 continue;
24378             }
24379             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24380             }
24381             var record = new Record(values, id);
24382             record.json = n;
24383             records[i] = record;
24384         }
24385         return {
24386             raw : o,
24387             success : success,
24388             records : records,
24389             totalRecords : totalRecords
24390         };
24391     }
24392 });/*
24393  * Based on:
24394  * Ext JS Library 1.1.1
24395  * Copyright(c) 2006-2007, Ext JS, LLC.
24396  *
24397  * Originally Released Under LGPL - original licence link has changed is not relivant.
24398  *
24399  * Fork - LGPL
24400  * <script type="text/javascript">
24401  */
24402
24403 /**
24404  * @class Roo.data.XmlReader
24405  * @extends Roo.data.DataReader
24406  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
24407  * based on mappings in a provided Roo.data.Record constructor.<br><br>
24408  * <p>
24409  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
24410  * header in the HTTP response must be set to "text/xml".</em>
24411  * <p>
24412  * Example code:
24413  * <pre><code>
24414 var RecordDef = Roo.data.Record.create([
24415    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24416    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24417 ]);
24418 var myReader = new Roo.data.XmlReader({
24419    totalRecords: "results", // The element which contains the total dataset size (optional)
24420    record: "row",           // The repeated element which contains row information
24421    id: "id"                 // The element within the row that provides an ID for the record (optional)
24422 }, RecordDef);
24423 </code></pre>
24424  * <p>
24425  * This would consume an XML file like this:
24426  * <pre><code>
24427 &lt;?xml?>
24428 &lt;dataset>
24429  &lt;results>2&lt;/results>
24430  &lt;row>
24431    &lt;id>1&lt;/id>
24432    &lt;name>Bill&lt;/name>
24433    &lt;occupation>Gardener&lt;/occupation>
24434  &lt;/row>
24435  &lt;row>
24436    &lt;id>2&lt;/id>
24437    &lt;name>Ben&lt;/name>
24438    &lt;occupation>Horticulturalist&lt;/occupation>
24439  &lt;/row>
24440 &lt;/dataset>
24441 </code></pre>
24442  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
24443  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24444  * paged from the remote server.
24445  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
24446  * @cfg {String} success The DomQuery path to the success attribute used by forms.
24447  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
24448  * a record identifier value.
24449  * @constructor
24450  * Create a new XmlReader
24451  * @param {Object} meta Metadata configuration options
24452  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
24453  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
24454  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
24455  */
24456 Roo.data.XmlReader = function(meta, recordType){
24457     meta = meta || {};
24458     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24459 };
24460 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
24461     /**
24462      * This method is only used by a DataProxy which has retrieved data from a remote server.
24463          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
24464          * to contain a method called 'responseXML' that returns an XML document object.
24465      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24466      * a cache of Roo.data.Records.
24467      */
24468     read : function(response){
24469         var doc = response.responseXML;
24470         if(!doc) {
24471             throw {message: "XmlReader.read: XML Document not available"};
24472         }
24473         return this.readRecords(doc);
24474     },
24475
24476     /**
24477      * Create a data block containing Roo.data.Records from an XML document.
24478          * @param {Object} doc A parsed XML document.
24479      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24480      * a cache of Roo.data.Records.
24481      */
24482     readRecords : function(doc){
24483         /**
24484          * After any data loads/reads, the raw XML Document is available for further custom processing.
24485          * @type XMLDocument
24486          */
24487         this.xmlData = doc;
24488         var root = doc.documentElement || doc;
24489         var q = Roo.DomQuery;
24490         var recordType = this.recordType, fields = recordType.prototype.fields;
24491         var sid = this.meta.id;
24492         var totalRecords = 0, success = true;
24493         if(this.meta.totalRecords){
24494             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
24495         }
24496         
24497         if(this.meta.success){
24498             var sv = q.selectValue(this.meta.success, root, true);
24499             success = sv !== false && sv !== 'false';
24500         }
24501         var records = [];
24502         var ns = q.select(this.meta.record, root);
24503         for(var i = 0, len = ns.length; i < len; i++) {
24504                 var n = ns[i];
24505                 var values = {};
24506                 var id = sid ? q.selectValue(sid, n) : undefined;
24507                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24508                     var f = fields.items[j];
24509                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
24510                     v = f.convert(v);
24511                     values[f.name] = v;
24512                 }
24513                 var record = new recordType(values, id);
24514                 record.node = n;
24515                 records[records.length] = record;
24516             }
24517
24518             return {
24519                 success : success,
24520                 records : records,
24521                 totalRecords : totalRecords || records.length
24522             };
24523     }
24524 });/*
24525  * Based on:
24526  * Ext JS Library 1.1.1
24527  * Copyright(c) 2006-2007, Ext JS, LLC.
24528  *
24529  * Originally Released Under LGPL - original licence link has changed is not relivant.
24530  *
24531  * Fork - LGPL
24532  * <script type="text/javascript">
24533  */
24534
24535 /**
24536  * @class Roo.data.ArrayReader
24537  * @extends Roo.data.DataReader
24538  * Data reader class to create an Array of Roo.data.Record objects from an Array.
24539  * Each element of that Array represents a row of data fields. The
24540  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
24541  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
24542  * <p>
24543  * Example code:.
24544  * <pre><code>
24545 var RecordDef = Roo.data.Record.create([
24546     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
24547     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
24548 ]);
24549 var myReader = new Roo.data.ArrayReader({
24550     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
24551 }, RecordDef);
24552 </code></pre>
24553  * <p>
24554  * This would consume an Array like this:
24555  * <pre><code>
24556 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
24557   </code></pre>
24558  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
24559  * @constructor
24560  * Create a new JsonReader
24561  * @param {Object} meta Metadata configuration options.
24562  * @param {Object} recordType Either an Array of field definition objects
24563  * as specified to {@link Roo.data.Record#create},
24564  * or an {@link Roo.data.Record} object
24565  * created using {@link Roo.data.Record#create}.
24566  */
24567 Roo.data.ArrayReader = function(meta, recordType){
24568     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
24569 };
24570
24571 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
24572     /**
24573      * Create a data block containing Roo.data.Records from an XML document.
24574      * @param {Object} o An Array of row objects which represents the dataset.
24575      * @return {Object} data A data block which is used by an Roo.data.Store object as
24576      * a cache of Roo.data.Records.
24577      */
24578     readRecords : function(o){
24579         var sid = this.meta ? this.meta.id : null;
24580         var recordType = this.recordType, fields = recordType.prototype.fields;
24581         var records = [];
24582         var root = o;
24583             for(var i = 0; i < root.length; i++){
24584                     var n = root[i];
24585                 var values = {};
24586                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
24587                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24588                 var f = fields.items[j];
24589                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
24590                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
24591                 v = f.convert(v);
24592                 values[f.name] = v;
24593             }
24594                 var record = new recordType(values, id);
24595                 record.json = n;
24596                 records[records.length] = record;
24597             }
24598             return {
24599                 records : records,
24600                 totalRecords : records.length
24601             };
24602     }
24603 });/*
24604  * Based on:
24605  * Ext JS Library 1.1.1
24606  * Copyright(c) 2006-2007, Ext JS, LLC.
24607  *
24608  * Originally Released Under LGPL - original licence link has changed is not relivant.
24609  *
24610  * Fork - LGPL
24611  * <script type="text/javascript">
24612  */
24613
24614
24615 /**
24616  * @class Roo.data.Tree
24617  * @extends Roo.util.Observable
24618  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
24619  * in the tree have most standard DOM functionality.
24620  * @constructor
24621  * @param {Node} root (optional) The root node
24622  */
24623 Roo.data.Tree = function(root){
24624    this.nodeHash = {};
24625    /**
24626     * The root node for this tree
24627     * @type Node
24628     */
24629    this.root = null;
24630    if(root){
24631        this.setRootNode(root);
24632    }
24633    this.addEvents({
24634        /**
24635         * @event append
24636         * Fires when a new child node is appended to a node in this tree.
24637         * @param {Tree} tree The owner tree
24638         * @param {Node} parent The parent node
24639         * @param {Node} node The newly appended node
24640         * @param {Number} index The index of the newly appended node
24641         */
24642        "append" : true,
24643        /**
24644         * @event remove
24645         * Fires when a child node is removed from a node in this tree.
24646         * @param {Tree} tree The owner tree
24647         * @param {Node} parent The parent node
24648         * @param {Node} node The child node removed
24649         */
24650        "remove" : true,
24651        /**
24652         * @event move
24653         * Fires when a node is moved to a new location in the tree
24654         * @param {Tree} tree The owner tree
24655         * @param {Node} node The node moved
24656         * @param {Node} oldParent The old parent of this node
24657         * @param {Node} newParent The new parent of this node
24658         * @param {Number} index The index it was moved to
24659         */
24660        "move" : true,
24661        /**
24662         * @event insert
24663         * Fires when a new child node is inserted in a node in this tree.
24664         * @param {Tree} tree The owner tree
24665         * @param {Node} parent The parent node
24666         * @param {Node} node The child node inserted
24667         * @param {Node} refNode The child node the node was inserted before
24668         */
24669        "insert" : true,
24670        /**
24671         * @event beforeappend
24672         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
24673         * @param {Tree} tree The owner tree
24674         * @param {Node} parent The parent node
24675         * @param {Node} node The child node to be appended
24676         */
24677        "beforeappend" : true,
24678        /**
24679         * @event beforeremove
24680         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
24681         * @param {Tree} tree The owner tree
24682         * @param {Node} parent The parent node
24683         * @param {Node} node The child node to be removed
24684         */
24685        "beforeremove" : true,
24686        /**
24687         * @event beforemove
24688         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
24689         * @param {Tree} tree The owner tree
24690         * @param {Node} node The node being moved
24691         * @param {Node} oldParent The parent of the node
24692         * @param {Node} newParent The new parent the node is moving to
24693         * @param {Number} index The index it is being moved to
24694         */
24695        "beforemove" : true,
24696        /**
24697         * @event beforeinsert
24698         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
24699         * @param {Tree} tree The owner tree
24700         * @param {Node} parent The parent node
24701         * @param {Node} node The child node to be inserted
24702         * @param {Node} refNode The child node the node is being inserted before
24703         */
24704        "beforeinsert" : true
24705    });
24706
24707     Roo.data.Tree.superclass.constructor.call(this);
24708 };
24709
24710 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
24711     pathSeparator: "/",
24712
24713     proxyNodeEvent : function(){
24714         return this.fireEvent.apply(this, arguments);
24715     },
24716
24717     /**
24718      * Returns the root node for this tree.
24719      * @return {Node}
24720      */
24721     getRootNode : function(){
24722         return this.root;
24723     },
24724
24725     /**
24726      * Sets the root node for this tree.
24727      * @param {Node} node
24728      * @return {Node}
24729      */
24730     setRootNode : function(node){
24731         this.root = node;
24732         node.ownerTree = this;
24733         node.isRoot = true;
24734         this.registerNode(node);
24735         return node;
24736     },
24737
24738     /**
24739      * Gets a node in this tree by its id.
24740      * @param {String} id
24741      * @return {Node}
24742      */
24743     getNodeById : function(id){
24744         return this.nodeHash[id];
24745     },
24746
24747     registerNode : function(node){
24748         this.nodeHash[node.id] = node;
24749     },
24750
24751     unregisterNode : function(node){
24752         delete this.nodeHash[node.id];
24753     },
24754
24755     toString : function(){
24756         return "[Tree"+(this.id?" "+this.id:"")+"]";
24757     }
24758 });
24759
24760 /**
24761  * @class Roo.data.Node
24762  * @extends Roo.util.Observable
24763  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
24764  * @cfg {String} id The id for this node. If one is not specified, one is generated.
24765  * @constructor
24766  * @param {Object} attributes The attributes/config for the node
24767  */
24768 Roo.data.Node = function(attributes){
24769     /**
24770      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
24771      * @type {Object}
24772      */
24773     this.attributes = attributes || {};
24774     this.leaf = this.attributes.leaf;
24775     /**
24776      * The node id. @type String
24777      */
24778     this.id = this.attributes.id;
24779     if(!this.id){
24780         this.id = Roo.id(null, "ynode-");
24781         this.attributes.id = this.id;
24782     }
24783      
24784     
24785     /**
24786      * All child nodes of this node. @type Array
24787      */
24788     this.childNodes = [];
24789     if(!this.childNodes.indexOf){ // indexOf is a must
24790         this.childNodes.indexOf = function(o){
24791             for(var i = 0, len = this.length; i < len; i++){
24792                 if(this[i] == o) {
24793                     return i;
24794                 }
24795             }
24796             return -1;
24797         };
24798     }
24799     /**
24800      * The parent node for this node. @type Node
24801      */
24802     this.parentNode = null;
24803     /**
24804      * The first direct child node of this node, or null if this node has no child nodes. @type Node
24805      */
24806     this.firstChild = null;
24807     /**
24808      * The last direct child node of this node, or null if this node has no child nodes. @type Node
24809      */
24810     this.lastChild = null;
24811     /**
24812      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
24813      */
24814     this.previousSibling = null;
24815     /**
24816      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
24817      */
24818     this.nextSibling = null;
24819
24820     this.addEvents({
24821        /**
24822         * @event append
24823         * Fires when a new child node is appended
24824         * @param {Tree} tree The owner tree
24825         * @param {Node} this This node
24826         * @param {Node} node The newly appended node
24827         * @param {Number} index The index of the newly appended node
24828         */
24829        "append" : true,
24830        /**
24831         * @event remove
24832         * Fires when a child node is removed
24833         * @param {Tree} tree The owner tree
24834         * @param {Node} this This node
24835         * @param {Node} node The removed node
24836         */
24837        "remove" : true,
24838        /**
24839         * @event move
24840         * Fires when this node is moved to a new location in the tree
24841         * @param {Tree} tree The owner tree
24842         * @param {Node} this This node
24843         * @param {Node} oldParent The old parent of this node
24844         * @param {Node} newParent The new parent of this node
24845         * @param {Number} index The index it was moved to
24846         */
24847        "move" : true,
24848        /**
24849         * @event insert
24850         * Fires when a new child node is inserted.
24851         * @param {Tree} tree The owner tree
24852         * @param {Node} this This node
24853         * @param {Node} node The child node inserted
24854         * @param {Node} refNode The child node the node was inserted before
24855         */
24856        "insert" : true,
24857        /**
24858         * @event beforeappend
24859         * Fires before a new child is appended, return false to cancel the append.
24860         * @param {Tree} tree The owner tree
24861         * @param {Node} this This node
24862         * @param {Node} node The child node to be appended
24863         */
24864        "beforeappend" : true,
24865        /**
24866         * @event beforeremove
24867         * Fires before a child is removed, return false to cancel the remove.
24868         * @param {Tree} tree The owner tree
24869         * @param {Node} this This node
24870         * @param {Node} node The child node to be removed
24871         */
24872        "beforeremove" : true,
24873        /**
24874         * @event beforemove
24875         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
24876         * @param {Tree} tree The owner tree
24877         * @param {Node} this This node
24878         * @param {Node} oldParent The parent of this node
24879         * @param {Node} newParent The new parent this node is moving to
24880         * @param {Number} index The index it is being moved to
24881         */
24882        "beforemove" : true,
24883        /**
24884         * @event beforeinsert
24885         * Fires before a new child is inserted, return false to cancel the insert.
24886         * @param {Tree} tree The owner tree
24887         * @param {Node} this This node
24888         * @param {Node} node The child node to be inserted
24889         * @param {Node} refNode The child node the node is being inserted before
24890         */
24891        "beforeinsert" : true
24892    });
24893     this.listeners = this.attributes.listeners;
24894     Roo.data.Node.superclass.constructor.call(this);
24895 };
24896
24897 Roo.extend(Roo.data.Node, Roo.util.Observable, {
24898     fireEvent : function(evtName){
24899         // first do standard event for this node
24900         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
24901             return false;
24902         }
24903         // then bubble it up to the tree if the event wasn't cancelled
24904         var ot = this.getOwnerTree();
24905         if(ot){
24906             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
24907                 return false;
24908             }
24909         }
24910         return true;
24911     },
24912
24913     /**
24914      * Returns true if this node is a leaf
24915      * @return {Boolean}
24916      */
24917     isLeaf : function(){
24918         return this.leaf === true;
24919     },
24920
24921     // private
24922     setFirstChild : function(node){
24923         this.firstChild = node;
24924     },
24925
24926     //private
24927     setLastChild : function(node){
24928         this.lastChild = node;
24929     },
24930
24931
24932     /**
24933      * Returns true if this node is the last child of its parent
24934      * @return {Boolean}
24935      */
24936     isLast : function(){
24937        return (!this.parentNode ? true : this.parentNode.lastChild == this);
24938     },
24939
24940     /**
24941      * Returns true if this node is the first child of its parent
24942      * @return {Boolean}
24943      */
24944     isFirst : function(){
24945        return (!this.parentNode ? true : this.parentNode.firstChild == this);
24946     },
24947
24948     hasChildNodes : function(){
24949         return !this.isLeaf() && this.childNodes.length > 0;
24950     },
24951
24952     /**
24953      * Insert node(s) as the last child node of this node.
24954      * @param {Node/Array} node The node or Array of nodes to append
24955      * @return {Node} The appended node if single append, or null if an array was passed
24956      */
24957     appendChild : function(node){
24958         var multi = false;
24959         if(node instanceof Array){
24960             multi = node;
24961         }else if(arguments.length > 1){
24962             multi = arguments;
24963         }
24964         // if passed an array or multiple args do them one by one
24965         if(multi){
24966             for(var i = 0, len = multi.length; i < len; i++) {
24967                 this.appendChild(multi[i]);
24968             }
24969         }else{
24970             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
24971                 return false;
24972             }
24973             var index = this.childNodes.length;
24974             var oldParent = node.parentNode;
24975             // it's a move, make sure we move it cleanly
24976             if(oldParent){
24977                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
24978                     return false;
24979                 }
24980                 oldParent.removeChild(node);
24981             }
24982             index = this.childNodes.length;
24983             if(index == 0){
24984                 this.setFirstChild(node);
24985             }
24986             this.childNodes.push(node);
24987             node.parentNode = this;
24988             var ps = this.childNodes[index-1];
24989             if(ps){
24990                 node.previousSibling = ps;
24991                 ps.nextSibling = node;
24992             }else{
24993                 node.previousSibling = null;
24994             }
24995             node.nextSibling = null;
24996             this.setLastChild(node);
24997             node.setOwnerTree(this.getOwnerTree());
24998             this.fireEvent("append", this.ownerTree, this, node, index);
24999             if(oldParent){
25000                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
25001             }
25002             return node;
25003         }
25004     },
25005
25006     /**
25007      * Removes a child node from this node.
25008      * @param {Node} node The node to remove
25009      * @return {Node} The removed node
25010      */
25011     removeChild : function(node){
25012         var index = this.childNodes.indexOf(node);
25013         if(index == -1){
25014             return false;
25015         }
25016         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
25017             return false;
25018         }
25019
25020         // remove it from childNodes collection
25021         this.childNodes.splice(index, 1);
25022
25023         // update siblings
25024         if(node.previousSibling){
25025             node.previousSibling.nextSibling = node.nextSibling;
25026         }
25027         if(node.nextSibling){
25028             node.nextSibling.previousSibling = node.previousSibling;
25029         }
25030
25031         // update child refs
25032         if(this.firstChild == node){
25033             this.setFirstChild(node.nextSibling);
25034         }
25035         if(this.lastChild == node){
25036             this.setLastChild(node.previousSibling);
25037         }
25038
25039         node.setOwnerTree(null);
25040         // clear any references from the node
25041         node.parentNode = null;
25042         node.previousSibling = null;
25043         node.nextSibling = null;
25044         this.fireEvent("remove", this.ownerTree, this, node);
25045         return node;
25046     },
25047
25048     /**
25049      * Inserts the first node before the second node in this nodes childNodes collection.
25050      * @param {Node} node The node to insert
25051      * @param {Node} refNode The node to insert before (if null the node is appended)
25052      * @return {Node} The inserted node
25053      */
25054     insertBefore : function(node, refNode){
25055         if(!refNode){ // like standard Dom, refNode can be null for append
25056             return this.appendChild(node);
25057         }
25058         // nothing to do
25059         if(node == refNode){
25060             return false;
25061         }
25062
25063         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
25064             return false;
25065         }
25066         var index = this.childNodes.indexOf(refNode);
25067         var oldParent = node.parentNode;
25068         var refIndex = index;
25069
25070         // when moving internally, indexes will change after remove
25071         if(oldParent == this && this.childNodes.indexOf(node) < index){
25072             refIndex--;
25073         }
25074
25075         // it's a move, make sure we move it cleanly
25076         if(oldParent){
25077             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
25078                 return false;
25079             }
25080             oldParent.removeChild(node);
25081         }
25082         if(refIndex == 0){
25083             this.setFirstChild(node);
25084         }
25085         this.childNodes.splice(refIndex, 0, node);
25086         node.parentNode = this;
25087         var ps = this.childNodes[refIndex-1];
25088         if(ps){
25089             node.previousSibling = ps;
25090             ps.nextSibling = node;
25091         }else{
25092             node.previousSibling = null;
25093         }
25094         node.nextSibling = refNode;
25095         refNode.previousSibling = node;
25096         node.setOwnerTree(this.getOwnerTree());
25097         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25098         if(oldParent){
25099             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25100         }
25101         return node;
25102     },
25103
25104     /**
25105      * Returns the child node at the specified index.
25106      * @param {Number} index
25107      * @return {Node}
25108      */
25109     item : function(index){
25110         return this.childNodes[index];
25111     },
25112
25113     /**
25114      * Replaces one child node in this node with another.
25115      * @param {Node} newChild The replacement node
25116      * @param {Node} oldChild The node to replace
25117      * @return {Node} The replaced node
25118      */
25119     replaceChild : function(newChild, oldChild){
25120         this.insertBefore(newChild, oldChild);
25121         this.removeChild(oldChild);
25122         return oldChild;
25123     },
25124
25125     /**
25126      * Returns the index of a child node
25127      * @param {Node} node
25128      * @return {Number} The index of the node or -1 if it was not found
25129      */
25130     indexOf : function(child){
25131         return this.childNodes.indexOf(child);
25132     },
25133
25134     /**
25135      * Returns the tree this node is in.
25136      * @return {Tree}
25137      */
25138     getOwnerTree : function(){
25139         // if it doesn't have one, look for one
25140         if(!this.ownerTree){
25141             var p = this;
25142             while(p){
25143                 if(p.ownerTree){
25144                     this.ownerTree = p.ownerTree;
25145                     break;
25146                 }
25147                 p = p.parentNode;
25148             }
25149         }
25150         return this.ownerTree;
25151     },
25152
25153     /**
25154      * Returns depth of this node (the root node has a depth of 0)
25155      * @return {Number}
25156      */
25157     getDepth : function(){
25158         var depth = 0;
25159         var p = this;
25160         while(p.parentNode){
25161             ++depth;
25162             p = p.parentNode;
25163         }
25164         return depth;
25165     },
25166
25167     // private
25168     setOwnerTree : function(tree){
25169         // if it's move, we need to update everyone
25170         if(tree != this.ownerTree){
25171             if(this.ownerTree){
25172                 this.ownerTree.unregisterNode(this);
25173             }
25174             this.ownerTree = tree;
25175             var cs = this.childNodes;
25176             for(var i = 0, len = cs.length; i < len; i++) {
25177                 cs[i].setOwnerTree(tree);
25178             }
25179             if(tree){
25180                 tree.registerNode(this);
25181             }
25182         }
25183     },
25184
25185     /**
25186      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25187      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25188      * @return {String} The path
25189      */
25190     getPath : function(attr){
25191         attr = attr || "id";
25192         var p = this.parentNode;
25193         var b = [this.attributes[attr]];
25194         while(p){
25195             b.unshift(p.attributes[attr]);
25196             p = p.parentNode;
25197         }
25198         var sep = this.getOwnerTree().pathSeparator;
25199         return sep + b.join(sep);
25200     },
25201
25202     /**
25203      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25204      * function call will be the scope provided or the current node. The arguments to the function
25205      * will be the args provided or the current node. If the function returns false at any point,
25206      * the bubble is stopped.
25207      * @param {Function} fn The function to call
25208      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25209      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25210      */
25211     bubble : function(fn, scope, args){
25212         var p = this;
25213         while(p){
25214             if(fn.call(scope || p, args || p) === false){
25215                 break;
25216             }
25217             p = p.parentNode;
25218         }
25219     },
25220
25221     /**
25222      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25223      * function call will be the scope provided or the current node. The arguments to the function
25224      * will be the args provided or the current node. If the function returns false at any point,
25225      * the cascade is stopped on that branch.
25226      * @param {Function} fn The function to call
25227      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25228      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25229      */
25230     cascade : function(fn, scope, args){
25231         if(fn.call(scope || this, args || this) !== false){
25232             var cs = this.childNodes;
25233             for(var i = 0, len = cs.length; i < len; i++) {
25234                 cs[i].cascade(fn, scope, args);
25235             }
25236         }
25237     },
25238
25239     /**
25240      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25241      * function call will be the scope provided or the current node. The arguments to the function
25242      * will be the args provided or the current node. If the function returns false at any point,
25243      * the iteration stops.
25244      * @param {Function} fn The function to call
25245      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25246      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25247      */
25248     eachChild : function(fn, scope, args){
25249         var cs = this.childNodes;
25250         for(var i = 0, len = cs.length; i < len; i++) {
25251                 if(fn.call(scope || this, args || cs[i]) === false){
25252                     break;
25253                 }
25254         }
25255     },
25256
25257     /**
25258      * Finds the first child that has the attribute with the specified value.
25259      * @param {String} attribute The attribute name
25260      * @param {Mixed} value The value to search for
25261      * @return {Node} The found child or null if none was found
25262      */
25263     findChild : function(attribute, value){
25264         var cs = this.childNodes;
25265         for(var i = 0, len = cs.length; i < len; i++) {
25266                 if(cs[i].attributes[attribute] == value){
25267                     return cs[i];
25268                 }
25269         }
25270         return null;
25271     },
25272
25273     /**
25274      * Finds the first child by a custom function. The child matches if the function passed
25275      * returns true.
25276      * @param {Function} fn
25277      * @param {Object} scope (optional)
25278      * @return {Node} The found child or null if none was found
25279      */
25280     findChildBy : function(fn, scope){
25281         var cs = this.childNodes;
25282         for(var i = 0, len = cs.length; i < len; i++) {
25283                 if(fn.call(scope||cs[i], cs[i]) === true){
25284                     return cs[i];
25285                 }
25286         }
25287         return null;
25288     },
25289
25290     /**
25291      * Sorts this nodes children using the supplied sort function
25292      * @param {Function} fn
25293      * @param {Object} scope (optional)
25294      */
25295     sort : function(fn, scope){
25296         var cs = this.childNodes;
25297         var len = cs.length;
25298         if(len > 0){
25299             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25300             cs.sort(sortFn);
25301             for(var i = 0; i < len; i++){
25302                 var n = cs[i];
25303                 n.previousSibling = cs[i-1];
25304                 n.nextSibling = cs[i+1];
25305                 if(i == 0){
25306                     this.setFirstChild(n);
25307                 }
25308                 if(i == len-1){
25309                     this.setLastChild(n);
25310                 }
25311             }
25312         }
25313     },
25314
25315     /**
25316      * Returns true if this node is an ancestor (at any point) of the passed node.
25317      * @param {Node} node
25318      * @return {Boolean}
25319      */
25320     contains : function(node){
25321         return node.isAncestor(this);
25322     },
25323
25324     /**
25325      * Returns true if the passed node is an ancestor (at any point) of this node.
25326      * @param {Node} node
25327      * @return {Boolean}
25328      */
25329     isAncestor : function(node){
25330         var p = this.parentNode;
25331         while(p){
25332             if(p == node){
25333                 return true;
25334             }
25335             p = p.parentNode;
25336         }
25337         return false;
25338     },
25339
25340     toString : function(){
25341         return "[Node"+(this.id?" "+this.id:"")+"]";
25342     }
25343 });/*
25344  * Based on:
25345  * Ext JS Library 1.1.1
25346  * Copyright(c) 2006-2007, Ext JS, LLC.
25347  *
25348  * Originally Released Under LGPL - original licence link has changed is not relivant.
25349  *
25350  * Fork - LGPL
25351  * <script type="text/javascript">
25352  */
25353  (function(){ 
25354 /**
25355  * @class Roo.Layer
25356  * @extends Roo.Element
25357  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
25358  * automatic maintaining of shadow/shim positions.
25359  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
25360  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
25361  * you can pass a string with a CSS class name. False turns off the shadow.
25362  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
25363  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
25364  * @cfg {String} cls CSS class to add to the element
25365  * @cfg {Number} zindex Starting z-index (defaults to 11000)
25366  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
25367  * @constructor
25368  * @param {Object} config An object with config options.
25369  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
25370  */
25371
25372 Roo.Layer = function(config, existingEl){
25373     config = config || {};
25374     var dh = Roo.DomHelper;
25375     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
25376     if(existingEl){
25377         this.dom = Roo.getDom(existingEl);
25378     }
25379     if(!this.dom){
25380         var o = config.dh || {tag: "div", cls: "x-layer"};
25381         this.dom = dh.append(pel, o);
25382     }
25383     if(config.cls){
25384         this.addClass(config.cls);
25385     }
25386     this.constrain = config.constrain !== false;
25387     this.visibilityMode = Roo.Element.VISIBILITY;
25388     if(config.id){
25389         this.id = this.dom.id = config.id;
25390     }else{
25391         this.id = Roo.id(this.dom);
25392     }
25393     this.zindex = config.zindex || this.getZIndex();
25394     this.position("absolute", this.zindex);
25395     if(config.shadow){
25396         this.shadowOffset = config.shadowOffset || 4;
25397         this.shadow = new Roo.Shadow({
25398             offset : this.shadowOffset,
25399             mode : config.shadow
25400         });
25401     }else{
25402         this.shadowOffset = 0;
25403     }
25404     this.useShim = config.shim !== false && Roo.useShims;
25405     this.useDisplay = config.useDisplay;
25406     this.hide();
25407 };
25408
25409 var supr = Roo.Element.prototype;
25410
25411 // shims are shared among layer to keep from having 100 iframes
25412 var shims = [];
25413
25414 Roo.extend(Roo.Layer, Roo.Element, {
25415
25416     getZIndex : function(){
25417         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
25418     },
25419
25420     getShim : function(){
25421         if(!this.useShim){
25422             return null;
25423         }
25424         if(this.shim){
25425             return this.shim;
25426         }
25427         var shim = shims.shift();
25428         if(!shim){
25429             shim = this.createShim();
25430             shim.enableDisplayMode('block');
25431             shim.dom.style.display = 'none';
25432             shim.dom.style.visibility = 'visible';
25433         }
25434         var pn = this.dom.parentNode;
25435         if(shim.dom.parentNode != pn){
25436             pn.insertBefore(shim.dom, this.dom);
25437         }
25438         shim.setStyle('z-index', this.getZIndex()-2);
25439         this.shim = shim;
25440         return shim;
25441     },
25442
25443     hideShim : function(){
25444         if(this.shim){
25445             this.shim.setDisplayed(false);
25446             shims.push(this.shim);
25447             delete this.shim;
25448         }
25449     },
25450
25451     disableShadow : function(){
25452         if(this.shadow){
25453             this.shadowDisabled = true;
25454             this.shadow.hide();
25455             this.lastShadowOffset = this.shadowOffset;
25456             this.shadowOffset = 0;
25457         }
25458     },
25459
25460     enableShadow : function(show){
25461         if(this.shadow){
25462             this.shadowDisabled = false;
25463             this.shadowOffset = this.lastShadowOffset;
25464             delete this.lastShadowOffset;
25465             if(show){
25466                 this.sync(true);
25467             }
25468         }
25469     },
25470
25471     // private
25472     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
25473     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
25474     sync : function(doShow){
25475         var sw = this.shadow;
25476         if(!this.updating && this.isVisible() && (sw || this.useShim)){
25477             var sh = this.getShim();
25478
25479             var w = this.getWidth(),
25480                 h = this.getHeight();
25481
25482             var l = this.getLeft(true),
25483                 t = this.getTop(true);
25484
25485             if(sw && !this.shadowDisabled){
25486                 if(doShow && !sw.isVisible()){
25487                     sw.show(this);
25488                 }else{
25489                     sw.realign(l, t, w, h);
25490                 }
25491                 if(sh){
25492                     if(doShow){
25493                        sh.show();
25494                     }
25495                     // fit the shim behind the shadow, so it is shimmed too
25496                     var a = sw.adjusts, s = sh.dom.style;
25497                     s.left = (Math.min(l, l+a.l))+"px";
25498                     s.top = (Math.min(t, t+a.t))+"px";
25499                     s.width = (w+a.w)+"px";
25500                     s.height = (h+a.h)+"px";
25501                 }
25502             }else if(sh){
25503                 if(doShow){
25504                    sh.show();
25505                 }
25506                 sh.setSize(w, h);
25507                 sh.setLeftTop(l, t);
25508             }
25509             
25510         }
25511     },
25512
25513     // private
25514     destroy : function(){
25515         this.hideShim();
25516         if(this.shadow){
25517             this.shadow.hide();
25518         }
25519         this.removeAllListeners();
25520         var pn = this.dom.parentNode;
25521         if(pn){
25522             pn.removeChild(this.dom);
25523         }
25524         Roo.Element.uncache(this.id);
25525     },
25526
25527     remove : function(){
25528         this.destroy();
25529     },
25530
25531     // private
25532     beginUpdate : function(){
25533         this.updating = true;
25534     },
25535
25536     // private
25537     endUpdate : function(){
25538         this.updating = false;
25539         this.sync(true);
25540     },
25541
25542     // private
25543     hideUnders : function(negOffset){
25544         if(this.shadow){
25545             this.shadow.hide();
25546         }
25547         this.hideShim();
25548     },
25549
25550     // private
25551     constrainXY : function(){
25552         if(this.constrain){
25553             var vw = Roo.lib.Dom.getViewWidth(),
25554                 vh = Roo.lib.Dom.getViewHeight();
25555             var s = Roo.get(document).getScroll();
25556
25557             var xy = this.getXY();
25558             var x = xy[0], y = xy[1];   
25559             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
25560             // only move it if it needs it
25561             var moved = false;
25562             // first validate right/bottom
25563             if((x + w) > vw+s.left){
25564                 x = vw - w - this.shadowOffset;
25565                 moved = true;
25566             }
25567             if((y + h) > vh+s.top){
25568                 y = vh - h - this.shadowOffset;
25569                 moved = true;
25570             }
25571             // then make sure top/left isn't negative
25572             if(x < s.left){
25573                 x = s.left;
25574                 moved = true;
25575             }
25576             if(y < s.top){
25577                 y = s.top;
25578                 moved = true;
25579             }
25580             if(moved){
25581                 if(this.avoidY){
25582                     var ay = this.avoidY;
25583                     if(y <= ay && (y+h) >= ay){
25584                         y = ay-h-5;   
25585                     }
25586                 }
25587                 xy = [x, y];
25588                 this.storeXY(xy);
25589                 supr.setXY.call(this, xy);
25590                 this.sync();
25591             }
25592         }
25593     },
25594
25595     isVisible : function(){
25596         return this.visible;    
25597     },
25598
25599     // private
25600     showAction : function(){
25601         this.visible = true; // track visibility to prevent getStyle calls
25602         if(this.useDisplay === true){
25603             this.setDisplayed("");
25604         }else if(this.lastXY){
25605             supr.setXY.call(this, this.lastXY);
25606         }else if(this.lastLT){
25607             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
25608         }
25609     },
25610
25611     // private
25612     hideAction : function(){
25613         this.visible = false;
25614         if(this.useDisplay === true){
25615             this.setDisplayed(false);
25616         }else{
25617             this.setLeftTop(-10000,-10000);
25618         }
25619     },
25620
25621     // overridden Element method
25622     setVisible : function(v, a, d, c, e){
25623         if(v){
25624             this.showAction();
25625         }
25626         if(a && v){
25627             var cb = function(){
25628                 this.sync(true);
25629                 if(c){
25630                     c();
25631                 }
25632             }.createDelegate(this);
25633             supr.setVisible.call(this, true, true, d, cb, e);
25634         }else{
25635             if(!v){
25636                 this.hideUnders(true);
25637             }
25638             var cb = c;
25639             if(a){
25640                 cb = function(){
25641                     this.hideAction();
25642                     if(c){
25643                         c();
25644                     }
25645                 }.createDelegate(this);
25646             }
25647             supr.setVisible.call(this, v, a, d, cb, e);
25648             if(v){
25649                 this.sync(true);
25650             }else if(!a){
25651                 this.hideAction();
25652             }
25653         }
25654     },
25655
25656     storeXY : function(xy){
25657         delete this.lastLT;
25658         this.lastXY = xy;
25659     },
25660
25661     storeLeftTop : function(left, top){
25662         delete this.lastXY;
25663         this.lastLT = [left, top];
25664     },
25665
25666     // private
25667     beforeFx : function(){
25668         this.beforeAction();
25669         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
25670     },
25671
25672     // private
25673     afterFx : function(){
25674         Roo.Layer.superclass.afterFx.apply(this, arguments);
25675         this.sync(this.isVisible());
25676     },
25677
25678     // private
25679     beforeAction : function(){
25680         if(!this.updating && this.shadow){
25681             this.shadow.hide();
25682         }
25683     },
25684
25685     // overridden Element method
25686     setLeft : function(left){
25687         this.storeLeftTop(left, this.getTop(true));
25688         supr.setLeft.apply(this, arguments);
25689         this.sync();
25690     },
25691
25692     setTop : function(top){
25693         this.storeLeftTop(this.getLeft(true), top);
25694         supr.setTop.apply(this, arguments);
25695         this.sync();
25696     },
25697
25698     setLeftTop : function(left, top){
25699         this.storeLeftTop(left, top);
25700         supr.setLeftTop.apply(this, arguments);
25701         this.sync();
25702     },
25703
25704     setXY : function(xy, a, d, c, e){
25705         this.fixDisplay();
25706         this.beforeAction();
25707         this.storeXY(xy);
25708         var cb = this.createCB(c);
25709         supr.setXY.call(this, xy, a, d, cb, e);
25710         if(!a){
25711             cb();
25712         }
25713     },
25714
25715     // private
25716     createCB : function(c){
25717         var el = this;
25718         return function(){
25719             el.constrainXY();
25720             el.sync(true);
25721             if(c){
25722                 c();
25723             }
25724         };
25725     },
25726
25727     // overridden Element method
25728     setX : function(x, a, d, c, e){
25729         this.setXY([x, this.getY()], a, d, c, e);
25730     },
25731
25732     // overridden Element method
25733     setY : function(y, a, d, c, e){
25734         this.setXY([this.getX(), y], a, d, c, e);
25735     },
25736
25737     // overridden Element method
25738     setSize : function(w, h, a, d, c, e){
25739         this.beforeAction();
25740         var cb = this.createCB(c);
25741         supr.setSize.call(this, w, h, a, d, cb, e);
25742         if(!a){
25743             cb();
25744         }
25745     },
25746
25747     // overridden Element method
25748     setWidth : function(w, a, d, c, e){
25749         this.beforeAction();
25750         var cb = this.createCB(c);
25751         supr.setWidth.call(this, w, a, d, cb, e);
25752         if(!a){
25753             cb();
25754         }
25755     },
25756
25757     // overridden Element method
25758     setHeight : function(h, a, d, c, e){
25759         this.beforeAction();
25760         var cb = this.createCB(c);
25761         supr.setHeight.call(this, h, a, d, cb, e);
25762         if(!a){
25763             cb();
25764         }
25765     },
25766
25767     // overridden Element method
25768     setBounds : function(x, y, w, h, a, d, c, e){
25769         this.beforeAction();
25770         var cb = this.createCB(c);
25771         if(!a){
25772             this.storeXY([x, y]);
25773             supr.setXY.call(this, [x, y]);
25774             supr.setSize.call(this, w, h, a, d, cb, e);
25775             cb();
25776         }else{
25777             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
25778         }
25779         return this;
25780     },
25781     
25782     /**
25783      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
25784      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
25785      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
25786      * @param {Number} zindex The new z-index to set
25787      * @return {this} The Layer
25788      */
25789     setZIndex : function(zindex){
25790         this.zindex = zindex;
25791         this.setStyle("z-index", zindex + 2);
25792         if(this.shadow){
25793             this.shadow.setZIndex(zindex + 1);
25794         }
25795         if(this.shim){
25796             this.shim.setStyle("z-index", zindex);
25797         }
25798     }
25799 });
25800 })();/*
25801  * Based on:
25802  * Ext JS Library 1.1.1
25803  * Copyright(c) 2006-2007, Ext JS, LLC.
25804  *
25805  * Originally Released Under LGPL - original licence link has changed is not relivant.
25806  *
25807  * Fork - LGPL
25808  * <script type="text/javascript">
25809  */
25810
25811
25812 /**
25813  * @class Roo.Shadow
25814  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25815  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25816  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25817  * @constructor
25818  * Create a new Shadow
25819  * @param {Object} config The config object
25820  */
25821 Roo.Shadow = function(config){
25822     Roo.apply(this, config);
25823     if(typeof this.mode != "string"){
25824         this.mode = this.defaultMode;
25825     }
25826     var o = this.offset, a = {h: 0};
25827     var rad = Math.floor(this.offset/2);
25828     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
25829         case "drop":
25830             a.w = 0;
25831             a.l = a.t = o;
25832             a.t -= 1;
25833             if(Roo.isIE){
25834                 a.l -= this.offset + rad;
25835                 a.t -= this.offset + rad;
25836                 a.w -= rad;
25837                 a.h -= rad;
25838                 a.t += 1;
25839             }
25840         break;
25841         case "sides":
25842             a.w = (o*2);
25843             a.l = -o;
25844             a.t = o-1;
25845             if(Roo.isIE){
25846                 a.l -= (this.offset - rad);
25847                 a.t -= this.offset + rad;
25848                 a.l += 1;
25849                 a.w -= (this.offset - rad)*2;
25850                 a.w -= rad + 1;
25851                 a.h -= 1;
25852             }
25853         break;
25854         case "frame":
25855             a.w = a.h = (o*2);
25856             a.l = a.t = -o;
25857             a.t += 1;
25858             a.h -= 2;
25859             if(Roo.isIE){
25860                 a.l -= (this.offset - rad);
25861                 a.t -= (this.offset - rad);
25862                 a.l += 1;
25863                 a.w -= (this.offset + rad + 1);
25864                 a.h -= (this.offset + rad);
25865                 a.h += 1;
25866             }
25867         break;
25868     };
25869
25870     this.adjusts = a;
25871 };
25872
25873 Roo.Shadow.prototype = {
25874     /**
25875      * @cfg {String} mode
25876      * The shadow display mode.  Supports the following options:<br />
25877      * sides: Shadow displays on both sides and bottom only<br />
25878      * frame: Shadow displays equally on all four sides<br />
25879      * drop: Traditional bottom-right drop shadow (default)
25880      */
25881     /**
25882      * @cfg {String} offset
25883      * The number of pixels to offset the shadow from the element (defaults to 4)
25884      */
25885     offset: 4,
25886
25887     // private
25888     defaultMode: "drop",
25889
25890     /**
25891      * Displays the shadow under the target element
25892      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
25893      */
25894     show : function(target){
25895         target = Roo.get(target);
25896         if(!this.el){
25897             this.el = Roo.Shadow.Pool.pull();
25898             if(this.el.dom.nextSibling != target.dom){
25899                 this.el.insertBefore(target);
25900             }
25901         }
25902         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
25903         if(Roo.isIE){
25904             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
25905         }
25906         this.realign(
25907             target.getLeft(true),
25908             target.getTop(true),
25909             target.getWidth(),
25910             target.getHeight()
25911         );
25912         this.el.dom.style.display = "block";
25913     },
25914
25915     /**
25916      * Returns true if the shadow is visible, else false
25917      */
25918     isVisible : function(){
25919         return this.el ? true : false;  
25920     },
25921
25922     /**
25923      * Direct alignment when values are already available. Show must be called at least once before
25924      * calling this method to ensure it is initialized.
25925      * @param {Number} left The target element left position
25926      * @param {Number} top The target element top position
25927      * @param {Number} width The target element width
25928      * @param {Number} height The target element height
25929      */
25930     realign : function(l, t, w, h){
25931         if(!this.el){
25932             return;
25933         }
25934         var a = this.adjusts, d = this.el.dom, s = d.style;
25935         var iea = 0;
25936         s.left = (l+a.l)+"px";
25937         s.top = (t+a.t)+"px";
25938         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
25939  
25940         if(s.width != sws || s.height != shs){
25941             s.width = sws;
25942             s.height = shs;
25943             if(!Roo.isIE){
25944                 var cn = d.childNodes;
25945                 var sww = Math.max(0, (sw-12))+"px";
25946                 cn[0].childNodes[1].style.width = sww;
25947                 cn[1].childNodes[1].style.width = sww;
25948                 cn[2].childNodes[1].style.width = sww;
25949                 cn[1].style.height = Math.max(0, (sh-12))+"px";
25950             }
25951         }
25952     },
25953
25954     /**
25955      * Hides this shadow
25956      */
25957     hide : function(){
25958         if(this.el){
25959             this.el.dom.style.display = "none";
25960             Roo.Shadow.Pool.push(this.el);
25961             delete this.el;
25962         }
25963     },
25964
25965     /**
25966      * Adjust the z-index of this shadow
25967      * @param {Number} zindex The new z-index
25968      */
25969     setZIndex : function(z){
25970         this.zIndex = z;
25971         if(this.el){
25972             this.el.setStyle("z-index", z);
25973         }
25974     }
25975 };
25976
25977 // Private utility class that manages the internal Shadow cache
25978 Roo.Shadow.Pool = function(){
25979     var p = [];
25980     var markup = Roo.isIE ?
25981                  '<div class="x-ie-shadow"></div>' :
25982                  '<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>';
25983     return {
25984         pull : function(){
25985             var sh = p.shift();
25986             if(!sh){
25987                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
25988                 sh.autoBoxAdjust = false;
25989             }
25990             return sh;
25991         },
25992
25993         push : function(sh){
25994             p.push(sh);
25995         }
25996     };
25997 }();/*
25998  * Based on:
25999  * Ext JS Library 1.1.1
26000  * Copyright(c) 2006-2007, Ext JS, LLC.
26001  *
26002  * Originally Released Under LGPL - original licence link has changed is not relivant.
26003  *
26004  * Fork - LGPL
26005  * <script type="text/javascript">
26006  */
26007
26008
26009 /**
26010  * @class Roo.SplitBar
26011  * @extends Roo.util.Observable
26012  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
26013  * <br><br>
26014  * Usage:
26015  * <pre><code>
26016 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
26017                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
26018 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
26019 split.minSize = 100;
26020 split.maxSize = 600;
26021 split.animate = true;
26022 split.on('moved', splitterMoved);
26023 </code></pre>
26024  * @constructor
26025  * Create a new SplitBar
26026  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
26027  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
26028  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26029  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
26030                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
26031                         position of the SplitBar).
26032  */
26033 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
26034     
26035     /** @private */
26036     this.el = Roo.get(dragElement, true);
26037     this.el.dom.unselectable = "on";
26038     /** @private */
26039     this.resizingEl = Roo.get(resizingElement, true);
26040
26041     /**
26042      * @private
26043      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26044      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
26045      * @type Number
26046      */
26047     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
26048     
26049     /**
26050      * The minimum size of the resizing element. (Defaults to 0)
26051      * @type Number
26052      */
26053     this.minSize = 0;
26054     
26055     /**
26056      * The maximum size of the resizing element. (Defaults to 2000)
26057      * @type Number
26058      */
26059     this.maxSize = 2000;
26060     
26061     /**
26062      * Whether to animate the transition to the new size
26063      * @type Boolean
26064      */
26065     this.animate = false;
26066     
26067     /**
26068      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
26069      * @type Boolean
26070      */
26071     this.useShim = false;
26072     
26073     /** @private */
26074     this.shim = null;
26075     
26076     if(!existingProxy){
26077         /** @private */
26078         this.proxy = Roo.SplitBar.createProxy(this.orientation);
26079     }else{
26080         this.proxy = Roo.get(existingProxy).dom;
26081     }
26082     /** @private */
26083     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26084     
26085     /** @private */
26086     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26087     
26088     /** @private */
26089     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26090     
26091     /** @private */
26092     this.dragSpecs = {};
26093     
26094     /**
26095      * @private The adapter to use to positon and resize elements
26096      */
26097     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26098     this.adapter.init(this);
26099     
26100     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26101         /** @private */
26102         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26103         this.el.addClass("x-splitbar-h");
26104     }else{
26105         /** @private */
26106         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26107         this.el.addClass("x-splitbar-v");
26108     }
26109     
26110     this.addEvents({
26111         /**
26112          * @event resize
26113          * Fires when the splitter is moved (alias for {@link #event-moved})
26114          * @param {Roo.SplitBar} this
26115          * @param {Number} newSize the new width or height
26116          */
26117         "resize" : true,
26118         /**
26119          * @event moved
26120          * Fires when the splitter is moved
26121          * @param {Roo.SplitBar} this
26122          * @param {Number} newSize the new width or height
26123          */
26124         "moved" : true,
26125         /**
26126          * @event beforeresize
26127          * Fires before the splitter is dragged
26128          * @param {Roo.SplitBar} this
26129          */
26130         "beforeresize" : true,
26131
26132         "beforeapply" : true
26133     });
26134
26135     Roo.util.Observable.call(this);
26136 };
26137
26138 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26139     onStartProxyDrag : function(x, y){
26140         this.fireEvent("beforeresize", this);
26141         if(!this.overlay){
26142             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26143             o.unselectable();
26144             o.enableDisplayMode("block");
26145             // all splitbars share the same overlay
26146             Roo.SplitBar.prototype.overlay = o;
26147         }
26148         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26149         this.overlay.show();
26150         Roo.get(this.proxy).setDisplayed("block");
26151         var size = this.adapter.getElementSize(this);
26152         this.activeMinSize = this.getMinimumSize();;
26153         this.activeMaxSize = this.getMaximumSize();;
26154         var c1 = size - this.activeMinSize;
26155         var c2 = Math.max(this.activeMaxSize - size, 0);
26156         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26157             this.dd.resetConstraints();
26158             this.dd.setXConstraint(
26159                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26160                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26161             );
26162             this.dd.setYConstraint(0, 0);
26163         }else{
26164             this.dd.resetConstraints();
26165             this.dd.setXConstraint(0, 0);
26166             this.dd.setYConstraint(
26167                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26168                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26169             );
26170          }
26171         this.dragSpecs.startSize = size;
26172         this.dragSpecs.startPoint = [x, y];
26173         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26174     },
26175     
26176     /** 
26177      * @private Called after the drag operation by the DDProxy
26178      */
26179     onEndProxyDrag : function(e){
26180         Roo.get(this.proxy).setDisplayed(false);
26181         var endPoint = Roo.lib.Event.getXY(e);
26182         if(this.overlay){
26183             this.overlay.hide();
26184         }
26185         var newSize;
26186         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26187             newSize = this.dragSpecs.startSize + 
26188                 (this.placement == Roo.SplitBar.LEFT ?
26189                     endPoint[0] - this.dragSpecs.startPoint[0] :
26190                     this.dragSpecs.startPoint[0] - endPoint[0]
26191                 );
26192         }else{
26193             newSize = this.dragSpecs.startSize + 
26194                 (this.placement == Roo.SplitBar.TOP ?
26195                     endPoint[1] - this.dragSpecs.startPoint[1] :
26196                     this.dragSpecs.startPoint[1] - endPoint[1]
26197                 );
26198         }
26199         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26200         if(newSize != this.dragSpecs.startSize){
26201             if(this.fireEvent('beforeapply', this, newSize) !== false){
26202                 this.adapter.setElementSize(this, newSize);
26203                 this.fireEvent("moved", this, newSize);
26204                 this.fireEvent("resize", this, newSize);
26205             }
26206         }
26207     },
26208     
26209     /**
26210      * Get the adapter this SplitBar uses
26211      * @return The adapter object
26212      */
26213     getAdapter : function(){
26214         return this.adapter;
26215     },
26216     
26217     /**
26218      * Set the adapter this SplitBar uses
26219      * @param {Object} adapter A SplitBar adapter object
26220      */
26221     setAdapter : function(adapter){
26222         this.adapter = adapter;
26223         this.adapter.init(this);
26224     },
26225     
26226     /**
26227      * Gets the minimum size for the resizing element
26228      * @return {Number} The minimum size
26229      */
26230     getMinimumSize : function(){
26231         return this.minSize;
26232     },
26233     
26234     /**
26235      * Sets the minimum size for the resizing element
26236      * @param {Number} minSize The minimum size
26237      */
26238     setMinimumSize : function(minSize){
26239         this.minSize = minSize;
26240     },
26241     
26242     /**
26243      * Gets the maximum size for the resizing element
26244      * @return {Number} The maximum size
26245      */
26246     getMaximumSize : function(){
26247         return this.maxSize;
26248     },
26249     
26250     /**
26251      * Sets the maximum size for the resizing element
26252      * @param {Number} maxSize The maximum size
26253      */
26254     setMaximumSize : function(maxSize){
26255         this.maxSize = maxSize;
26256     },
26257     
26258     /**
26259      * Sets the initialize size for the resizing element
26260      * @param {Number} size The initial size
26261      */
26262     setCurrentSize : function(size){
26263         var oldAnimate = this.animate;
26264         this.animate = false;
26265         this.adapter.setElementSize(this, size);
26266         this.animate = oldAnimate;
26267     },
26268     
26269     /**
26270      * Destroy this splitbar. 
26271      * @param {Boolean} removeEl True to remove the element
26272      */
26273     destroy : function(removeEl){
26274         if(this.shim){
26275             this.shim.remove();
26276         }
26277         this.dd.unreg();
26278         this.proxy.parentNode.removeChild(this.proxy);
26279         if(removeEl){
26280             this.el.remove();
26281         }
26282     }
26283 });
26284
26285 /**
26286  * @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.
26287  */
26288 Roo.SplitBar.createProxy = function(dir){
26289     var proxy = new Roo.Element(document.createElement("div"));
26290     proxy.unselectable();
26291     var cls = 'x-splitbar-proxy';
26292     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26293     document.body.appendChild(proxy.dom);
26294     return proxy.dom;
26295 };
26296
26297 /** 
26298  * @class Roo.SplitBar.BasicLayoutAdapter
26299  * Default Adapter. It assumes the splitter and resizing element are not positioned
26300  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26301  */
26302 Roo.SplitBar.BasicLayoutAdapter = function(){
26303 };
26304
26305 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26306     // do nothing for now
26307     init : function(s){
26308     
26309     },
26310     /**
26311      * Called before drag operations to get the current size of the resizing element. 
26312      * @param {Roo.SplitBar} s The SplitBar using this adapter
26313      */
26314      getElementSize : function(s){
26315         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26316             return s.resizingEl.getWidth();
26317         }else{
26318             return s.resizingEl.getHeight();
26319         }
26320     },
26321     
26322     /**
26323      * Called after drag operations to set the size of the resizing element.
26324      * @param {Roo.SplitBar} s The SplitBar using this adapter
26325      * @param {Number} newSize The new size to set
26326      * @param {Function} onComplete A function to be invoked when resizing is complete
26327      */
26328     setElementSize : function(s, newSize, onComplete){
26329         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26330             if(!s.animate){
26331                 s.resizingEl.setWidth(newSize);
26332                 if(onComplete){
26333                     onComplete(s, newSize);
26334                 }
26335             }else{
26336                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26337             }
26338         }else{
26339             
26340             if(!s.animate){
26341                 s.resizingEl.setHeight(newSize);
26342                 if(onComplete){
26343                     onComplete(s, newSize);
26344                 }
26345             }else{
26346                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26347             }
26348         }
26349     }
26350 };
26351
26352 /** 
26353  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26354  * @extends Roo.SplitBar.BasicLayoutAdapter
26355  * Adapter that  moves the splitter element to align with the resized sizing element. 
26356  * Used with an absolute positioned SplitBar.
26357  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26358  * document.body, make sure you assign an id to the body element.
26359  */
26360 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26361     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26362     this.container = Roo.get(container);
26363 };
26364
26365 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26366     init : function(s){
26367         this.basic.init(s);
26368     },
26369     
26370     getElementSize : function(s){
26371         return this.basic.getElementSize(s);
26372     },
26373     
26374     setElementSize : function(s, newSize, onComplete){
26375         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26376     },
26377     
26378     moveSplitter : function(s){
26379         var yes = Roo.SplitBar;
26380         switch(s.placement){
26381             case yes.LEFT:
26382                 s.el.setX(s.resizingEl.getRight());
26383                 break;
26384             case yes.RIGHT:
26385                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26386                 break;
26387             case yes.TOP:
26388                 s.el.setY(s.resizingEl.getBottom());
26389                 break;
26390             case yes.BOTTOM:
26391                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26392                 break;
26393         }
26394     }
26395 };
26396
26397 /**
26398  * Orientation constant - Create a vertical SplitBar
26399  * @static
26400  * @type Number
26401  */
26402 Roo.SplitBar.VERTICAL = 1;
26403
26404 /**
26405  * Orientation constant - Create a horizontal SplitBar
26406  * @static
26407  * @type Number
26408  */
26409 Roo.SplitBar.HORIZONTAL = 2;
26410
26411 /**
26412  * Placement constant - The resizing element is to the left of the splitter element
26413  * @static
26414  * @type Number
26415  */
26416 Roo.SplitBar.LEFT = 1;
26417
26418 /**
26419  * Placement constant - The resizing element is to the right of the splitter element
26420  * @static
26421  * @type Number
26422  */
26423 Roo.SplitBar.RIGHT = 2;
26424
26425 /**
26426  * Placement constant - The resizing element is positioned above the splitter element
26427  * @static
26428  * @type Number
26429  */
26430 Roo.SplitBar.TOP = 3;
26431
26432 /**
26433  * Placement constant - The resizing element is positioned under splitter element
26434  * @static
26435  * @type Number
26436  */
26437 Roo.SplitBar.BOTTOM = 4;
26438 /*
26439  * Based on:
26440  * Ext JS Library 1.1.1
26441  * Copyright(c) 2006-2007, Ext JS, LLC.
26442  *
26443  * Originally Released Under LGPL - original licence link has changed is not relivant.
26444  *
26445  * Fork - LGPL
26446  * <script type="text/javascript">
26447  */
26448
26449 /**
26450  * @class Roo.View
26451  * @extends Roo.util.Observable
26452  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26453  * This class also supports single and multi selection modes. <br>
26454  * Create a data model bound view:
26455  <pre><code>
26456  var store = new Roo.data.Store(...);
26457
26458  var view = new Roo.View({
26459     el : "my-element",
26460     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26461  
26462     singleSelect: true,
26463     selectedClass: "ydataview-selected",
26464     store: store
26465  });
26466
26467  // listen for node click?
26468  view.on("click", function(vw, index, node, e){
26469  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26470  });
26471
26472  // load XML data
26473  dataModel.load("foobar.xml");
26474  </code></pre>
26475  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26476  * <br><br>
26477  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26478  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26479  * 
26480  * Note: old style constructor is still suported (container, template, config)
26481  * 
26482  * @constructor
26483  * Create a new View
26484  * @param {Object} config The config object
26485  * 
26486  */
26487 Roo.View = function(config, depreciated_tpl, depreciated_config){
26488     
26489     this.parent = false;
26490     
26491     if (typeof(depreciated_tpl) == 'undefined') {
26492         // new way.. - universal constructor.
26493         Roo.apply(this, config);
26494         this.el  = Roo.get(this.el);
26495     } else {
26496         // old format..
26497         this.el  = Roo.get(config);
26498         this.tpl = depreciated_tpl;
26499         Roo.apply(this, depreciated_config);
26500     }
26501     this.wrapEl  = this.el.wrap().wrap();
26502     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26503     
26504     
26505     if(typeof(this.tpl) == "string"){
26506         this.tpl = new Roo.Template(this.tpl);
26507     } else {
26508         // support xtype ctors..
26509         this.tpl = new Roo.factory(this.tpl, Roo);
26510     }
26511     
26512     
26513     this.tpl.compile();
26514     
26515     /** @private */
26516     this.addEvents({
26517         /**
26518          * @event beforeclick
26519          * Fires before a click is processed. Returns false to cancel the default action.
26520          * @param {Roo.View} this
26521          * @param {Number} index The index of the target node
26522          * @param {HTMLElement} node The target node
26523          * @param {Roo.EventObject} e The raw event object
26524          */
26525             "beforeclick" : true,
26526         /**
26527          * @event click
26528          * Fires when a template node is clicked.
26529          * @param {Roo.View} this
26530          * @param {Number} index The index of the target node
26531          * @param {HTMLElement} node The target node
26532          * @param {Roo.EventObject} e The raw event object
26533          */
26534             "click" : true,
26535         /**
26536          * @event dblclick
26537          * Fires when a template node is double clicked.
26538          * @param {Roo.View} this
26539          * @param {Number} index The index of the target node
26540          * @param {HTMLElement} node The target node
26541          * @param {Roo.EventObject} e The raw event object
26542          */
26543             "dblclick" : true,
26544         /**
26545          * @event contextmenu
26546          * Fires when a template node is right clicked.
26547          * @param {Roo.View} this
26548          * @param {Number} index The index of the target node
26549          * @param {HTMLElement} node The target node
26550          * @param {Roo.EventObject} e The raw event object
26551          */
26552             "contextmenu" : true,
26553         /**
26554          * @event selectionchange
26555          * Fires when the selected nodes change.
26556          * @param {Roo.View} this
26557          * @param {Array} selections Array of the selected nodes
26558          */
26559             "selectionchange" : true,
26560     
26561         /**
26562          * @event beforeselect
26563          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26564          * @param {Roo.View} this
26565          * @param {HTMLElement} node The node to be selected
26566          * @param {Array} selections Array of currently selected nodes
26567          */
26568             "beforeselect" : true,
26569         /**
26570          * @event preparedata
26571          * Fires on every row to render, to allow you to change the data.
26572          * @param {Roo.View} this
26573          * @param {Object} data to be rendered (change this)
26574          */
26575           "preparedata" : true
26576           
26577           
26578         });
26579
26580
26581
26582     this.el.on({
26583         "click": this.onClick,
26584         "dblclick": this.onDblClick,
26585         "contextmenu": this.onContextMenu,
26586         scope:this
26587     });
26588
26589     this.selections = [];
26590     this.nodes = [];
26591     this.cmp = new Roo.CompositeElementLite([]);
26592     if(this.store){
26593         this.store = Roo.factory(this.store, Roo.data);
26594         this.setStore(this.store, true);
26595     }
26596     
26597     if ( this.footer && this.footer.xtype) {
26598            
26599          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26600         
26601         this.footer.dataSource = this.store;
26602         this.footer.container = fctr;
26603         this.footer = Roo.factory(this.footer, Roo);
26604         fctr.insertFirst(this.el);
26605         
26606         // this is a bit insane - as the paging toolbar seems to detach the el..
26607 //        dom.parentNode.parentNode.parentNode
26608          // they get detached?
26609     }
26610     
26611     
26612     Roo.View.superclass.constructor.call(this);
26613     
26614     
26615 };
26616
26617 Roo.extend(Roo.View, Roo.util.Observable, {
26618     
26619      /**
26620      * @cfg {Roo.data.Store} store Data store to load data from.
26621      */
26622     store : false,
26623     
26624     /**
26625      * @cfg {String|Roo.Element} el The container element.
26626      */
26627     el : '',
26628     
26629     /**
26630      * @cfg {String|Roo.Template} tpl The template used by this View 
26631      */
26632     tpl : false,
26633     /**
26634      * @cfg {String} dataName the named area of the template to use as the data area
26635      *                          Works with domtemplates roo-name="name"
26636      */
26637     dataName: false,
26638     /**
26639      * @cfg {String} selectedClass The css class to add to selected nodes
26640      */
26641     selectedClass : "x-view-selected",
26642      /**
26643      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26644      */
26645     emptyText : "",
26646     
26647     /**
26648      * @cfg {String} text to display on mask (default Loading)
26649      */
26650     mask : false,
26651     /**
26652      * @cfg {Boolean} multiSelect Allow multiple selection
26653      */
26654     multiSelect : false,
26655     /**
26656      * @cfg {Boolean} singleSelect Allow single selection
26657      */
26658     singleSelect:  false,
26659     
26660     /**
26661      * @cfg {Boolean} toggleSelect - selecting 
26662      */
26663     toggleSelect : false,
26664     
26665     /**
26666      * @cfg {Boolean} tickable - selecting 
26667      */
26668     tickable : false,
26669     
26670     /**
26671      * Returns the element this view is bound to.
26672      * @return {Roo.Element}
26673      */
26674     getEl : function(){
26675         return this.wrapEl;
26676     },
26677     
26678     
26679
26680     /**
26681      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26682      */
26683     refresh : function(){
26684         //Roo.log('refresh');
26685         var t = this.tpl;
26686         
26687         // if we are using something like 'domtemplate', then
26688         // the what gets used is:
26689         // t.applySubtemplate(NAME, data, wrapping data..)
26690         // the outer template then get' applied with
26691         //     the store 'extra data'
26692         // and the body get's added to the
26693         //      roo-name="data" node?
26694         //      <span class='roo-tpl-{name}'></span> ?????
26695         
26696         
26697         
26698         this.clearSelections();
26699         this.el.update("");
26700         var html = [];
26701         var records = this.store.getRange();
26702         if(records.length < 1) {
26703             
26704             // is this valid??  = should it render a template??
26705             
26706             this.el.update(this.emptyText);
26707             return;
26708         }
26709         var el = this.el;
26710         if (this.dataName) {
26711             this.el.update(t.apply(this.store.meta)); //????
26712             el = this.el.child('.roo-tpl-' + this.dataName);
26713         }
26714         
26715         for(var i = 0, len = records.length; i < len; i++){
26716             var data = this.prepareData(records[i].data, i, records[i]);
26717             this.fireEvent("preparedata", this, data, i, records[i]);
26718             
26719             var d = Roo.apply({}, data);
26720             
26721             if(this.tickable){
26722                 Roo.apply(d, {'roo-id' : Roo.id()});
26723                 
26724                 var _this = this;
26725             
26726                 Roo.each(this.parent.item, function(item){
26727                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26728                         return;
26729                     }
26730                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26731                 });
26732             }
26733             
26734             html[html.length] = Roo.util.Format.trim(
26735                 this.dataName ?
26736                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26737                     t.apply(d)
26738             );
26739         }
26740         
26741         
26742         
26743         el.update(html.join(""));
26744         this.nodes = el.dom.childNodes;
26745         this.updateIndexes(0);
26746     },
26747     
26748
26749     /**
26750      * Function to override to reformat the data that is sent to
26751      * the template for each node.
26752      * DEPRICATED - use the preparedata event handler.
26753      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26754      * a JSON object for an UpdateManager bound view).
26755      */
26756     prepareData : function(data, index, record)
26757     {
26758         this.fireEvent("preparedata", this, data, index, record);
26759         return data;
26760     },
26761
26762     onUpdate : function(ds, record){
26763         // Roo.log('on update');   
26764         this.clearSelections();
26765         var index = this.store.indexOf(record);
26766         var n = this.nodes[index];
26767         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26768         n.parentNode.removeChild(n);
26769         this.updateIndexes(index, index);
26770     },
26771
26772     
26773     
26774 // --------- FIXME     
26775     onAdd : function(ds, records, index)
26776     {
26777         //Roo.log(['on Add', ds, records, index] );        
26778         this.clearSelections();
26779         if(this.nodes.length == 0){
26780             this.refresh();
26781             return;
26782         }
26783         var n = this.nodes[index];
26784         for(var i = 0, len = records.length; i < len; i++){
26785             var d = this.prepareData(records[i].data, i, records[i]);
26786             if(n){
26787                 this.tpl.insertBefore(n, d);
26788             }else{
26789                 
26790                 this.tpl.append(this.el, d);
26791             }
26792         }
26793         this.updateIndexes(index);
26794     },
26795
26796     onRemove : function(ds, record, index){
26797        // Roo.log('onRemove');
26798         this.clearSelections();
26799         var el = this.dataName  ?
26800             this.el.child('.roo-tpl-' + this.dataName) :
26801             this.el; 
26802         
26803         el.dom.removeChild(this.nodes[index]);
26804         this.updateIndexes(index);
26805     },
26806
26807     /**
26808      * Refresh an individual node.
26809      * @param {Number} index
26810      */
26811     refreshNode : function(index){
26812         this.onUpdate(this.store, this.store.getAt(index));
26813     },
26814
26815     updateIndexes : function(startIndex, endIndex){
26816         var ns = this.nodes;
26817         startIndex = startIndex || 0;
26818         endIndex = endIndex || ns.length - 1;
26819         for(var i = startIndex; i <= endIndex; i++){
26820             ns[i].nodeIndex = i;
26821         }
26822     },
26823
26824     /**
26825      * Changes the data store this view uses and refresh the view.
26826      * @param {Store} store
26827      */
26828     setStore : function(store, initial){
26829         if(!initial && this.store){
26830             this.store.un("datachanged", this.refresh);
26831             this.store.un("add", this.onAdd);
26832             this.store.un("remove", this.onRemove);
26833             this.store.un("update", this.onUpdate);
26834             this.store.un("clear", this.refresh);
26835             this.store.un("beforeload", this.onBeforeLoad);
26836             this.store.un("load", this.onLoad);
26837             this.store.un("loadexception", this.onLoad);
26838         }
26839         if(store){
26840           
26841             store.on("datachanged", this.refresh, this);
26842             store.on("add", this.onAdd, this);
26843             store.on("remove", this.onRemove, this);
26844             store.on("update", this.onUpdate, this);
26845             store.on("clear", this.refresh, this);
26846             store.on("beforeload", this.onBeforeLoad, this);
26847             store.on("load", this.onLoad, this);
26848             store.on("loadexception", this.onLoad, this);
26849         }
26850         
26851         if(store){
26852             this.refresh();
26853         }
26854     },
26855     /**
26856      * onbeforeLoad - masks the loading area.
26857      *
26858      */
26859     onBeforeLoad : function(store,opts)
26860     {
26861          //Roo.log('onBeforeLoad');   
26862         if (!opts.add) {
26863             this.el.update("");
26864         }
26865         this.el.mask(this.mask ? this.mask : "Loading" ); 
26866     },
26867     onLoad : function ()
26868     {
26869         this.el.unmask();
26870     },
26871     
26872
26873     /**
26874      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
26875      * @param {HTMLElement} node
26876      * @return {HTMLElement} The template node
26877      */
26878     findItemFromChild : function(node){
26879         var el = this.dataName  ?
26880             this.el.child('.roo-tpl-' + this.dataName,true) :
26881             this.el.dom; 
26882         
26883         if(!node || node.parentNode == el){
26884                     return node;
26885             }
26886             var p = node.parentNode;
26887             while(p && p != el){
26888             if(p.parentNode == el){
26889                 return p;
26890             }
26891             p = p.parentNode;
26892         }
26893             return null;
26894     },
26895
26896     /** @ignore */
26897     onClick : function(e){
26898         var item = this.findItemFromChild(e.getTarget());
26899         if(item){
26900             var index = this.indexOf(item);
26901             if(this.onItemClick(item, index, e) !== false){
26902                 this.fireEvent("click", this, index, item, e);
26903             }
26904         }else{
26905             this.clearSelections();
26906         }
26907     },
26908
26909     /** @ignore */
26910     onContextMenu : function(e){
26911         var item = this.findItemFromChild(e.getTarget());
26912         if(item){
26913             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
26914         }
26915     },
26916
26917     /** @ignore */
26918     onDblClick : function(e){
26919         var item = this.findItemFromChild(e.getTarget());
26920         if(item){
26921             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
26922         }
26923     },
26924
26925     onItemClick : function(item, index, e)
26926     {
26927         if(this.fireEvent("beforeclick", this, index, item, e) === false){
26928             return false;
26929         }
26930         if (this.toggleSelect) {
26931             var m = this.isSelected(item) ? 'unselect' : 'select';
26932             //Roo.log(m);
26933             var _t = this;
26934             _t[m](item, true, false);
26935             return true;
26936         }
26937         if(this.multiSelect || this.singleSelect){
26938             if(this.multiSelect && e.shiftKey && this.lastSelection){
26939                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
26940             }else{
26941                 this.select(item, this.multiSelect && e.ctrlKey);
26942                 this.lastSelection = item;
26943             }
26944             
26945             if(!this.tickable){
26946                 e.preventDefault();
26947             }
26948             
26949         }
26950         return true;
26951     },
26952
26953     /**
26954      * Get the number of selected nodes.
26955      * @return {Number}
26956      */
26957     getSelectionCount : function(){
26958         return this.selections.length;
26959     },
26960
26961     /**
26962      * Get the currently selected nodes.
26963      * @return {Array} An array of HTMLElements
26964      */
26965     getSelectedNodes : function(){
26966         return this.selections;
26967     },
26968
26969     /**
26970      * Get the indexes of the selected nodes.
26971      * @return {Array}
26972      */
26973     getSelectedIndexes : function(){
26974         var indexes = [], s = this.selections;
26975         for(var i = 0, len = s.length; i < len; i++){
26976             indexes.push(s[i].nodeIndex);
26977         }
26978         return indexes;
26979     },
26980
26981     /**
26982      * Clear all selections
26983      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
26984      */
26985     clearSelections : function(suppressEvent){
26986         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
26987             this.cmp.elements = this.selections;
26988             this.cmp.removeClass(this.selectedClass);
26989             this.selections = [];
26990             if(!suppressEvent){
26991                 this.fireEvent("selectionchange", this, this.selections);
26992             }
26993         }
26994     },
26995
26996     /**
26997      * Returns true if the passed node is selected
26998      * @param {HTMLElement/Number} node The node or node index
26999      * @return {Boolean}
27000      */
27001     isSelected : function(node){
27002         var s = this.selections;
27003         if(s.length < 1){
27004             return false;
27005         }
27006         node = this.getNode(node);
27007         return s.indexOf(node) !== -1;
27008     },
27009
27010     /**
27011      * Selects nodes.
27012      * @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
27013      * @param {Boolean} keepExisting (optional) true to keep existing selections
27014      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27015      */
27016     select : function(nodeInfo, keepExisting, suppressEvent){
27017         if(nodeInfo instanceof Array){
27018             if(!keepExisting){
27019                 this.clearSelections(true);
27020             }
27021             for(var i = 0, len = nodeInfo.length; i < len; i++){
27022                 this.select(nodeInfo[i], true, true);
27023             }
27024             return;
27025         } 
27026         var node = this.getNode(nodeInfo);
27027         if(!node || this.isSelected(node)){
27028             return; // already selected.
27029         }
27030         if(!keepExisting){
27031             this.clearSelections(true);
27032         }
27033         
27034         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27035             Roo.fly(node).addClass(this.selectedClass);
27036             this.selections.push(node);
27037             if(!suppressEvent){
27038                 this.fireEvent("selectionchange", this, this.selections);
27039             }
27040         }
27041         
27042         
27043     },
27044       /**
27045      * Unselects nodes.
27046      * @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
27047      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
27048      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27049      */
27050     unselect : function(nodeInfo, keepExisting, suppressEvent)
27051     {
27052         if(nodeInfo instanceof Array){
27053             Roo.each(this.selections, function(s) {
27054                 this.unselect(s, nodeInfo);
27055             }, this);
27056             return;
27057         }
27058         var node = this.getNode(nodeInfo);
27059         if(!node || !this.isSelected(node)){
27060             //Roo.log("not selected");
27061             return; // not selected.
27062         }
27063         // fireevent???
27064         var ns = [];
27065         Roo.each(this.selections, function(s) {
27066             if (s == node ) {
27067                 Roo.fly(node).removeClass(this.selectedClass);
27068
27069                 return;
27070             }
27071             ns.push(s);
27072         },this);
27073         
27074         this.selections= ns;
27075         this.fireEvent("selectionchange", this, this.selections);
27076     },
27077
27078     /**
27079      * Gets a template node.
27080      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27081      * @return {HTMLElement} The node or null if it wasn't found
27082      */
27083     getNode : function(nodeInfo){
27084         if(typeof nodeInfo == "string"){
27085             return document.getElementById(nodeInfo);
27086         }else if(typeof nodeInfo == "number"){
27087             return this.nodes[nodeInfo];
27088         }
27089         return nodeInfo;
27090     },
27091
27092     /**
27093      * Gets a range template nodes.
27094      * @param {Number} startIndex
27095      * @param {Number} endIndex
27096      * @return {Array} An array of nodes
27097      */
27098     getNodes : function(start, end){
27099         var ns = this.nodes;
27100         start = start || 0;
27101         end = typeof end == "undefined" ? ns.length - 1 : end;
27102         var nodes = [];
27103         if(start <= end){
27104             for(var i = start; i <= end; i++){
27105                 nodes.push(ns[i]);
27106             }
27107         } else{
27108             for(var i = start; i >= end; i--){
27109                 nodes.push(ns[i]);
27110             }
27111         }
27112         return nodes;
27113     },
27114
27115     /**
27116      * Finds the index of the passed node
27117      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27118      * @return {Number} The index of the node or -1
27119      */
27120     indexOf : function(node){
27121         node = this.getNode(node);
27122         if(typeof node.nodeIndex == "number"){
27123             return node.nodeIndex;
27124         }
27125         var ns = this.nodes;
27126         for(var i = 0, len = ns.length; i < len; i++){
27127             if(ns[i] == node){
27128                 return i;
27129             }
27130         }
27131         return -1;
27132     }
27133 });
27134 /*
27135  * Based on:
27136  * Ext JS Library 1.1.1
27137  * Copyright(c) 2006-2007, Ext JS, LLC.
27138  *
27139  * Originally Released Under LGPL - original licence link has changed is not relivant.
27140  *
27141  * Fork - LGPL
27142  * <script type="text/javascript">
27143  */
27144
27145 /**
27146  * @class Roo.JsonView
27147  * @extends Roo.View
27148  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27149 <pre><code>
27150 var view = new Roo.JsonView({
27151     container: "my-element",
27152     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27153     multiSelect: true, 
27154     jsonRoot: "data" 
27155 });
27156
27157 // listen for node click?
27158 view.on("click", function(vw, index, node, e){
27159     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27160 });
27161
27162 // direct load of JSON data
27163 view.load("foobar.php");
27164
27165 // Example from my blog list
27166 var tpl = new Roo.Template(
27167     '&lt;div class="entry"&gt;' +
27168     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27169     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27170     "&lt;/div&gt;&lt;hr /&gt;"
27171 );
27172
27173 var moreView = new Roo.JsonView({
27174     container :  "entry-list", 
27175     template : tpl,
27176     jsonRoot: "posts"
27177 });
27178 moreView.on("beforerender", this.sortEntries, this);
27179 moreView.load({
27180     url: "/blog/get-posts.php",
27181     params: "allposts=true",
27182     text: "Loading Blog Entries..."
27183 });
27184 </code></pre>
27185
27186 * Note: old code is supported with arguments : (container, template, config)
27187
27188
27189  * @constructor
27190  * Create a new JsonView
27191  * 
27192  * @param {Object} config The config object
27193  * 
27194  */
27195 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27196     
27197     
27198     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27199
27200     var um = this.el.getUpdateManager();
27201     um.setRenderer(this);
27202     um.on("update", this.onLoad, this);
27203     um.on("failure", this.onLoadException, this);
27204
27205     /**
27206      * @event beforerender
27207      * Fires before rendering of the downloaded JSON data.
27208      * @param {Roo.JsonView} this
27209      * @param {Object} data The JSON data loaded
27210      */
27211     /**
27212      * @event load
27213      * Fires when data is loaded.
27214      * @param {Roo.JsonView} this
27215      * @param {Object} data The JSON data loaded
27216      * @param {Object} response The raw Connect response object
27217      */
27218     /**
27219      * @event loadexception
27220      * Fires when loading fails.
27221      * @param {Roo.JsonView} this
27222      * @param {Object} response The raw Connect response object
27223      */
27224     this.addEvents({
27225         'beforerender' : true,
27226         'load' : true,
27227         'loadexception' : true
27228     });
27229 };
27230 Roo.extend(Roo.JsonView, Roo.View, {
27231     /**
27232      * @type {String} The root property in the loaded JSON object that contains the data
27233      */
27234     jsonRoot : "",
27235
27236     /**
27237      * Refreshes the view.
27238      */
27239     refresh : function(){
27240         this.clearSelections();
27241         this.el.update("");
27242         var html = [];
27243         var o = this.jsonData;
27244         if(o && o.length > 0){
27245             for(var i = 0, len = o.length; i < len; i++){
27246                 var data = this.prepareData(o[i], i, o);
27247                 html[html.length] = this.tpl.apply(data);
27248             }
27249         }else{
27250             html.push(this.emptyText);
27251         }
27252         this.el.update(html.join(""));
27253         this.nodes = this.el.dom.childNodes;
27254         this.updateIndexes(0);
27255     },
27256
27257     /**
27258      * 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.
27259      * @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:
27260      <pre><code>
27261      view.load({
27262          url: "your-url.php",
27263          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27264          callback: yourFunction,
27265          scope: yourObject, //(optional scope)
27266          discardUrl: false,
27267          nocache: false,
27268          text: "Loading...",
27269          timeout: 30,
27270          scripts: false
27271      });
27272      </code></pre>
27273      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27274      * 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.
27275      * @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}
27276      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27277      * @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.
27278      */
27279     load : function(){
27280         var um = this.el.getUpdateManager();
27281         um.update.apply(um, arguments);
27282     },
27283
27284     // note - render is a standard framework call...
27285     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27286     render : function(el, response){
27287         
27288         this.clearSelections();
27289         this.el.update("");
27290         var o;
27291         try{
27292             if (response != '') {
27293                 o = Roo.util.JSON.decode(response.responseText);
27294                 if(this.jsonRoot){
27295                     
27296                     o = o[this.jsonRoot];
27297                 }
27298             }
27299         } catch(e){
27300         }
27301         /**
27302          * The current JSON data or null
27303          */
27304         this.jsonData = o;
27305         this.beforeRender();
27306         this.refresh();
27307     },
27308
27309 /**
27310  * Get the number of records in the current JSON dataset
27311  * @return {Number}
27312  */
27313     getCount : function(){
27314         return this.jsonData ? this.jsonData.length : 0;
27315     },
27316
27317 /**
27318  * Returns the JSON object for the specified node(s)
27319  * @param {HTMLElement/Array} node The node or an array of nodes
27320  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27321  * you get the JSON object for the node
27322  */
27323     getNodeData : function(node){
27324         if(node instanceof Array){
27325             var data = [];
27326             for(var i = 0, len = node.length; i < len; i++){
27327                 data.push(this.getNodeData(node[i]));
27328             }
27329             return data;
27330         }
27331         return this.jsonData[this.indexOf(node)] || null;
27332     },
27333
27334     beforeRender : function(){
27335         this.snapshot = this.jsonData;
27336         if(this.sortInfo){
27337             this.sort.apply(this, this.sortInfo);
27338         }
27339         this.fireEvent("beforerender", this, this.jsonData);
27340     },
27341
27342     onLoad : function(el, o){
27343         this.fireEvent("load", this, this.jsonData, o);
27344     },
27345
27346     onLoadException : function(el, o){
27347         this.fireEvent("loadexception", this, o);
27348     },
27349
27350 /**
27351  * Filter the data by a specific property.
27352  * @param {String} property A property on your JSON objects
27353  * @param {String/RegExp} value Either string that the property values
27354  * should start with, or a RegExp to test against the property
27355  */
27356     filter : function(property, value){
27357         if(this.jsonData){
27358             var data = [];
27359             var ss = this.snapshot;
27360             if(typeof value == "string"){
27361                 var vlen = value.length;
27362                 if(vlen == 0){
27363                     this.clearFilter();
27364                     return;
27365                 }
27366                 value = value.toLowerCase();
27367                 for(var i = 0, len = ss.length; i < len; i++){
27368                     var o = ss[i];
27369                     if(o[property].substr(0, vlen).toLowerCase() == value){
27370                         data.push(o);
27371                     }
27372                 }
27373             } else if(value.exec){ // regex?
27374                 for(var i = 0, len = ss.length; i < len; i++){
27375                     var o = ss[i];
27376                     if(value.test(o[property])){
27377                         data.push(o);
27378                     }
27379                 }
27380             } else{
27381                 return;
27382             }
27383             this.jsonData = data;
27384             this.refresh();
27385         }
27386     },
27387
27388 /**
27389  * Filter by a function. The passed function will be called with each
27390  * object in the current dataset. If the function returns true the value is kept,
27391  * otherwise it is filtered.
27392  * @param {Function} fn
27393  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27394  */
27395     filterBy : function(fn, scope){
27396         if(this.jsonData){
27397             var data = [];
27398             var ss = this.snapshot;
27399             for(var i = 0, len = ss.length; i < len; i++){
27400                 var o = ss[i];
27401                 if(fn.call(scope || this, o)){
27402                     data.push(o);
27403                 }
27404             }
27405             this.jsonData = data;
27406             this.refresh();
27407         }
27408     },
27409
27410 /**
27411  * Clears the current filter.
27412  */
27413     clearFilter : function(){
27414         if(this.snapshot && this.jsonData != this.snapshot){
27415             this.jsonData = this.snapshot;
27416             this.refresh();
27417         }
27418     },
27419
27420
27421 /**
27422  * Sorts the data for this view and refreshes it.
27423  * @param {String} property A property on your JSON objects to sort on
27424  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27425  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27426  */
27427     sort : function(property, dir, sortType){
27428         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27429         if(this.jsonData){
27430             var p = property;
27431             var dsc = dir && dir.toLowerCase() == "desc";
27432             var f = function(o1, o2){
27433                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27434                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27435                 ;
27436                 if(v1 < v2){
27437                     return dsc ? +1 : -1;
27438                 } else if(v1 > v2){
27439                     return dsc ? -1 : +1;
27440                 } else{
27441                     return 0;
27442                 }
27443             };
27444             this.jsonData.sort(f);
27445             this.refresh();
27446             if(this.jsonData != this.snapshot){
27447                 this.snapshot.sort(f);
27448             }
27449         }
27450     }
27451 });/*
27452  * Based on:
27453  * Ext JS Library 1.1.1
27454  * Copyright(c) 2006-2007, Ext JS, LLC.
27455  *
27456  * Originally Released Under LGPL - original licence link has changed is not relivant.
27457  *
27458  * Fork - LGPL
27459  * <script type="text/javascript">
27460  */
27461  
27462
27463 /**
27464  * @class Roo.ColorPalette
27465  * @extends Roo.Component
27466  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27467  * Here's an example of typical usage:
27468  * <pre><code>
27469 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27470 cp.render('my-div');
27471
27472 cp.on('select', function(palette, selColor){
27473     // do something with selColor
27474 });
27475 </code></pre>
27476  * @constructor
27477  * Create a new ColorPalette
27478  * @param {Object} config The config object
27479  */
27480 Roo.ColorPalette = function(config){
27481     Roo.ColorPalette.superclass.constructor.call(this, config);
27482     this.addEvents({
27483         /**
27484              * @event select
27485              * Fires when a color is selected
27486              * @param {ColorPalette} this
27487              * @param {String} color The 6-digit color hex code (without the # symbol)
27488              */
27489         select: true
27490     });
27491
27492     if(this.handler){
27493         this.on("select", this.handler, this.scope, true);
27494     }
27495 };
27496 Roo.extend(Roo.ColorPalette, Roo.Component, {
27497     /**
27498      * @cfg {String} itemCls
27499      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27500      */
27501     itemCls : "x-color-palette",
27502     /**
27503      * @cfg {String} value
27504      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27505      * the hex codes are case-sensitive.
27506      */
27507     value : null,
27508     clickEvent:'click',
27509     // private
27510     ctype: "Roo.ColorPalette",
27511
27512     /**
27513      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27514      */
27515     allowReselect : false,
27516
27517     /**
27518      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27519      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27520      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27521      * of colors with the width setting until the box is symmetrical.</p>
27522      * <p>You can override individual colors if needed:</p>
27523      * <pre><code>
27524 var cp = new Roo.ColorPalette();
27525 cp.colors[0] = "FF0000";  // change the first box to red
27526 </code></pre>
27527
27528 Or you can provide a custom array of your own for complete control:
27529 <pre><code>
27530 var cp = new Roo.ColorPalette();
27531 cp.colors = ["000000", "993300", "333300"];
27532 </code></pre>
27533      * @type Array
27534      */
27535     colors : [
27536         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27537         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27538         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27539         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27540         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27541     ],
27542
27543     // private
27544     onRender : function(container, position){
27545         var t = new Roo.MasterTemplate(
27546             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27547         );
27548         var c = this.colors;
27549         for(var i = 0, len = c.length; i < len; i++){
27550             t.add([c[i]]);
27551         }
27552         var el = document.createElement("div");
27553         el.className = this.itemCls;
27554         t.overwrite(el);
27555         container.dom.insertBefore(el, position);
27556         this.el = Roo.get(el);
27557         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27558         if(this.clickEvent != 'click'){
27559             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27560         }
27561     },
27562
27563     // private
27564     afterRender : function(){
27565         Roo.ColorPalette.superclass.afterRender.call(this);
27566         if(this.value){
27567             var s = this.value;
27568             this.value = null;
27569             this.select(s);
27570         }
27571     },
27572
27573     // private
27574     handleClick : function(e, t){
27575         e.preventDefault();
27576         if(!this.disabled){
27577             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27578             this.select(c.toUpperCase());
27579         }
27580     },
27581
27582     /**
27583      * Selects the specified color in the palette (fires the select event)
27584      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27585      */
27586     select : function(color){
27587         color = color.replace("#", "");
27588         if(color != this.value || this.allowReselect){
27589             var el = this.el;
27590             if(this.value){
27591                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27592             }
27593             el.child("a.color-"+color).addClass("x-color-palette-sel");
27594             this.value = color;
27595             this.fireEvent("select", this, color);
27596         }
27597     }
27598 });/*
27599  * Based on:
27600  * Ext JS Library 1.1.1
27601  * Copyright(c) 2006-2007, Ext JS, LLC.
27602  *
27603  * Originally Released Under LGPL - original licence link has changed is not relivant.
27604  *
27605  * Fork - LGPL
27606  * <script type="text/javascript">
27607  */
27608  
27609 /**
27610  * @class Roo.DatePicker
27611  * @extends Roo.Component
27612  * Simple date picker class.
27613  * @constructor
27614  * Create a new DatePicker
27615  * @param {Object} config The config object
27616  */
27617 Roo.DatePicker = function(config){
27618     Roo.DatePicker.superclass.constructor.call(this, config);
27619
27620     this.value = config && config.value ?
27621                  config.value.clearTime() : new Date().clearTime();
27622
27623     this.addEvents({
27624         /**
27625              * @event select
27626              * Fires when a date is selected
27627              * @param {DatePicker} this
27628              * @param {Date} date The selected date
27629              */
27630         'select': true,
27631         /**
27632              * @event monthchange
27633              * Fires when the displayed month changes 
27634              * @param {DatePicker} this
27635              * @param {Date} date The selected month
27636              */
27637         'monthchange': true
27638     });
27639
27640     if(this.handler){
27641         this.on("select", this.handler,  this.scope || this);
27642     }
27643     // build the disabledDatesRE
27644     if(!this.disabledDatesRE && this.disabledDates){
27645         var dd = this.disabledDates;
27646         var re = "(?:";
27647         for(var i = 0; i < dd.length; i++){
27648             re += dd[i];
27649             if(i != dd.length-1) {
27650                 re += "|";
27651             }
27652         }
27653         this.disabledDatesRE = new RegExp(re + ")");
27654     }
27655 };
27656
27657 Roo.extend(Roo.DatePicker, Roo.Component, {
27658     /**
27659      * @cfg {String} todayText
27660      * The text to display on the button that selects the current date (defaults to "Today")
27661      */
27662     todayText : "Today",
27663     /**
27664      * @cfg {String} okText
27665      * The text to display on the ok button
27666      */
27667     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27668     /**
27669      * @cfg {String} cancelText
27670      * The text to display on the cancel button
27671      */
27672     cancelText : "Cancel",
27673     /**
27674      * @cfg {String} todayTip
27675      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27676      */
27677     todayTip : "{0} (Spacebar)",
27678     /**
27679      * @cfg {Date} minDate
27680      * Minimum allowable date (JavaScript date object, defaults to null)
27681      */
27682     minDate : null,
27683     /**
27684      * @cfg {Date} maxDate
27685      * Maximum allowable date (JavaScript date object, defaults to null)
27686      */
27687     maxDate : null,
27688     /**
27689      * @cfg {String} minText
27690      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27691      */
27692     minText : "This date is before the minimum date",
27693     /**
27694      * @cfg {String} maxText
27695      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27696      */
27697     maxText : "This date is after the maximum date",
27698     /**
27699      * @cfg {String} format
27700      * The default date format string which can be overriden for localization support.  The format must be
27701      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27702      */
27703     format : "m/d/y",
27704     /**
27705      * @cfg {Array} disabledDays
27706      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27707      */
27708     disabledDays : null,
27709     /**
27710      * @cfg {String} disabledDaysText
27711      * The tooltip to display when the date falls on a disabled day (defaults to "")
27712      */
27713     disabledDaysText : "",
27714     /**
27715      * @cfg {RegExp} disabledDatesRE
27716      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27717      */
27718     disabledDatesRE : null,
27719     /**
27720      * @cfg {String} disabledDatesText
27721      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27722      */
27723     disabledDatesText : "",
27724     /**
27725      * @cfg {Boolean} constrainToViewport
27726      * True to constrain the date picker to the viewport (defaults to true)
27727      */
27728     constrainToViewport : true,
27729     /**
27730      * @cfg {Array} monthNames
27731      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27732      */
27733     monthNames : Date.monthNames,
27734     /**
27735      * @cfg {Array} dayNames
27736      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27737      */
27738     dayNames : Date.dayNames,
27739     /**
27740      * @cfg {String} nextText
27741      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27742      */
27743     nextText: 'Next Month (Control+Right)',
27744     /**
27745      * @cfg {String} prevText
27746      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27747      */
27748     prevText: 'Previous Month (Control+Left)',
27749     /**
27750      * @cfg {String} monthYearText
27751      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27752      */
27753     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27754     /**
27755      * @cfg {Number} startDay
27756      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27757      */
27758     startDay : 0,
27759     /**
27760      * @cfg {Bool} showClear
27761      * Show a clear button (usefull for date form elements that can be blank.)
27762      */
27763     
27764     showClear: false,
27765     
27766     /**
27767      * Sets the value of the date field
27768      * @param {Date} value The date to set
27769      */
27770     setValue : function(value){
27771         var old = this.value;
27772         
27773         if (typeof(value) == 'string') {
27774          
27775             value = Date.parseDate(value, this.format);
27776         }
27777         if (!value) {
27778             value = new Date();
27779         }
27780         
27781         this.value = value.clearTime(true);
27782         if(this.el){
27783             this.update(this.value);
27784         }
27785     },
27786
27787     /**
27788      * Gets the current selected value of the date field
27789      * @return {Date} The selected date
27790      */
27791     getValue : function(){
27792         return this.value;
27793     },
27794
27795     // private
27796     focus : function(){
27797         if(this.el){
27798             this.update(this.activeDate);
27799         }
27800     },
27801
27802     // privateval
27803     onRender : function(container, position){
27804         
27805         var m = [
27806              '<table cellspacing="0">',
27807                 '<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>',
27808                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27809         var dn = this.dayNames;
27810         for(var i = 0; i < 7; i++){
27811             var d = this.startDay+i;
27812             if(d > 6){
27813                 d = d-7;
27814             }
27815             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27816         }
27817         m[m.length] = "</tr></thead><tbody><tr>";
27818         for(var i = 0; i < 42; i++) {
27819             if(i % 7 == 0 && i != 0){
27820                 m[m.length] = "</tr><tr>";
27821             }
27822             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27823         }
27824         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27825             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27826
27827         var el = document.createElement("div");
27828         el.className = "x-date-picker";
27829         el.innerHTML = m.join("");
27830
27831         container.dom.insertBefore(el, position);
27832
27833         this.el = Roo.get(el);
27834         this.eventEl = Roo.get(el.firstChild);
27835
27836         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
27837             handler: this.showPrevMonth,
27838             scope: this,
27839             preventDefault:true,
27840             stopDefault:true
27841         });
27842
27843         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
27844             handler: this.showNextMonth,
27845             scope: this,
27846             preventDefault:true,
27847             stopDefault:true
27848         });
27849
27850         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
27851
27852         this.monthPicker = this.el.down('div.x-date-mp');
27853         this.monthPicker.enableDisplayMode('block');
27854         
27855         var kn = new Roo.KeyNav(this.eventEl, {
27856             "left" : function(e){
27857                 e.ctrlKey ?
27858                     this.showPrevMonth() :
27859                     this.update(this.activeDate.add("d", -1));
27860             },
27861
27862             "right" : function(e){
27863                 e.ctrlKey ?
27864                     this.showNextMonth() :
27865                     this.update(this.activeDate.add("d", 1));
27866             },
27867
27868             "up" : function(e){
27869                 e.ctrlKey ?
27870                     this.showNextYear() :
27871                     this.update(this.activeDate.add("d", -7));
27872             },
27873
27874             "down" : function(e){
27875                 e.ctrlKey ?
27876                     this.showPrevYear() :
27877                     this.update(this.activeDate.add("d", 7));
27878             },
27879
27880             "pageUp" : function(e){
27881                 this.showNextMonth();
27882             },
27883
27884             "pageDown" : function(e){
27885                 this.showPrevMonth();
27886             },
27887
27888             "enter" : function(e){
27889                 e.stopPropagation();
27890                 return true;
27891             },
27892
27893             scope : this
27894         });
27895
27896         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
27897
27898         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
27899
27900         this.el.unselectable();
27901         
27902         this.cells = this.el.select("table.x-date-inner tbody td");
27903         this.textNodes = this.el.query("table.x-date-inner tbody span");
27904
27905         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
27906             text: "&#160;",
27907             tooltip: this.monthYearText
27908         });
27909
27910         this.mbtn.on('click', this.showMonthPicker, this);
27911         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
27912
27913
27914         var today = (new Date()).dateFormat(this.format);
27915         
27916         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
27917         if (this.showClear) {
27918             baseTb.add( new Roo.Toolbar.Fill());
27919         }
27920         baseTb.add({
27921             text: String.format(this.todayText, today),
27922             tooltip: String.format(this.todayTip, today),
27923             handler: this.selectToday,
27924             scope: this
27925         });
27926         
27927         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
27928             
27929         //});
27930         if (this.showClear) {
27931             
27932             baseTb.add( new Roo.Toolbar.Fill());
27933             baseTb.add({
27934                 text: '&#160;',
27935                 cls: 'x-btn-icon x-btn-clear',
27936                 handler: function() {
27937                     //this.value = '';
27938                     this.fireEvent("select", this, '');
27939                 },
27940                 scope: this
27941             });
27942         }
27943         
27944         
27945         if(Roo.isIE){
27946             this.el.repaint();
27947         }
27948         this.update(this.value);
27949     },
27950
27951     createMonthPicker : function(){
27952         if(!this.monthPicker.dom.firstChild){
27953             var buf = ['<table border="0" cellspacing="0">'];
27954             for(var i = 0; i < 6; i++){
27955                 buf.push(
27956                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
27957                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
27958                     i == 0 ?
27959                     '<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>' :
27960                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
27961                 );
27962             }
27963             buf.push(
27964                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
27965                     this.okText,
27966                     '</button><button type="button" class="x-date-mp-cancel">',
27967                     this.cancelText,
27968                     '</button></td></tr>',
27969                 '</table>'
27970             );
27971             this.monthPicker.update(buf.join(''));
27972             this.monthPicker.on('click', this.onMonthClick, this);
27973             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
27974
27975             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
27976             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
27977
27978             this.mpMonths.each(function(m, a, i){
27979                 i += 1;
27980                 if((i%2) == 0){
27981                     m.dom.xmonth = 5 + Math.round(i * .5);
27982                 }else{
27983                     m.dom.xmonth = Math.round((i-1) * .5);
27984                 }
27985             });
27986         }
27987     },
27988
27989     showMonthPicker : function(){
27990         this.createMonthPicker();
27991         var size = this.el.getSize();
27992         this.monthPicker.setSize(size);
27993         this.monthPicker.child('table').setSize(size);
27994
27995         this.mpSelMonth = (this.activeDate || this.value).getMonth();
27996         this.updateMPMonth(this.mpSelMonth);
27997         this.mpSelYear = (this.activeDate || this.value).getFullYear();
27998         this.updateMPYear(this.mpSelYear);
27999
28000         this.monthPicker.slideIn('t', {duration:.2});
28001     },
28002
28003     updateMPYear : function(y){
28004         this.mpyear = y;
28005         var ys = this.mpYears.elements;
28006         for(var i = 1; i <= 10; i++){
28007             var td = ys[i-1], y2;
28008             if((i%2) == 0){
28009                 y2 = y + Math.round(i * .5);
28010                 td.firstChild.innerHTML = y2;
28011                 td.xyear = y2;
28012             }else{
28013                 y2 = y - (5-Math.round(i * .5));
28014                 td.firstChild.innerHTML = y2;
28015                 td.xyear = y2;
28016             }
28017             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
28018         }
28019     },
28020
28021     updateMPMonth : function(sm){
28022         this.mpMonths.each(function(m, a, i){
28023             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
28024         });
28025     },
28026
28027     selectMPMonth: function(m){
28028         
28029     },
28030
28031     onMonthClick : function(e, t){
28032         e.stopEvent();
28033         var el = new Roo.Element(t), pn;
28034         if(el.is('button.x-date-mp-cancel')){
28035             this.hideMonthPicker();
28036         }
28037         else if(el.is('button.x-date-mp-ok')){
28038             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28039             this.hideMonthPicker();
28040         }
28041         else if(pn = el.up('td.x-date-mp-month', 2)){
28042             this.mpMonths.removeClass('x-date-mp-sel');
28043             pn.addClass('x-date-mp-sel');
28044             this.mpSelMonth = pn.dom.xmonth;
28045         }
28046         else if(pn = el.up('td.x-date-mp-year', 2)){
28047             this.mpYears.removeClass('x-date-mp-sel');
28048             pn.addClass('x-date-mp-sel');
28049             this.mpSelYear = pn.dom.xyear;
28050         }
28051         else if(el.is('a.x-date-mp-prev')){
28052             this.updateMPYear(this.mpyear-10);
28053         }
28054         else if(el.is('a.x-date-mp-next')){
28055             this.updateMPYear(this.mpyear+10);
28056         }
28057     },
28058
28059     onMonthDblClick : function(e, t){
28060         e.stopEvent();
28061         var el = new Roo.Element(t), pn;
28062         if(pn = el.up('td.x-date-mp-month', 2)){
28063             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
28064             this.hideMonthPicker();
28065         }
28066         else if(pn = el.up('td.x-date-mp-year', 2)){
28067             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28068             this.hideMonthPicker();
28069         }
28070     },
28071
28072     hideMonthPicker : function(disableAnim){
28073         if(this.monthPicker){
28074             if(disableAnim === true){
28075                 this.monthPicker.hide();
28076             }else{
28077                 this.monthPicker.slideOut('t', {duration:.2});
28078             }
28079         }
28080     },
28081
28082     // private
28083     showPrevMonth : function(e){
28084         this.update(this.activeDate.add("mo", -1));
28085     },
28086
28087     // private
28088     showNextMonth : function(e){
28089         this.update(this.activeDate.add("mo", 1));
28090     },
28091
28092     // private
28093     showPrevYear : function(){
28094         this.update(this.activeDate.add("y", -1));
28095     },
28096
28097     // private
28098     showNextYear : function(){
28099         this.update(this.activeDate.add("y", 1));
28100     },
28101
28102     // private
28103     handleMouseWheel : function(e){
28104         var delta = e.getWheelDelta();
28105         if(delta > 0){
28106             this.showPrevMonth();
28107             e.stopEvent();
28108         } else if(delta < 0){
28109             this.showNextMonth();
28110             e.stopEvent();
28111         }
28112     },
28113
28114     // private
28115     handleDateClick : function(e, t){
28116         e.stopEvent();
28117         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28118             this.setValue(new Date(t.dateValue));
28119             this.fireEvent("select", this, this.value);
28120         }
28121     },
28122
28123     // private
28124     selectToday : function(){
28125         this.setValue(new Date().clearTime());
28126         this.fireEvent("select", this, this.value);
28127     },
28128
28129     // private
28130     update : function(date)
28131     {
28132         var vd = this.activeDate;
28133         this.activeDate = date;
28134         if(vd && this.el){
28135             var t = date.getTime();
28136             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28137                 this.cells.removeClass("x-date-selected");
28138                 this.cells.each(function(c){
28139                    if(c.dom.firstChild.dateValue == t){
28140                        c.addClass("x-date-selected");
28141                        setTimeout(function(){
28142                             try{c.dom.firstChild.focus();}catch(e){}
28143                        }, 50);
28144                        return false;
28145                    }
28146                 });
28147                 return;
28148             }
28149         }
28150         
28151         var days = date.getDaysInMonth();
28152         var firstOfMonth = date.getFirstDateOfMonth();
28153         var startingPos = firstOfMonth.getDay()-this.startDay;
28154
28155         if(startingPos <= this.startDay){
28156             startingPos += 7;
28157         }
28158
28159         var pm = date.add("mo", -1);
28160         var prevStart = pm.getDaysInMonth()-startingPos;
28161
28162         var cells = this.cells.elements;
28163         var textEls = this.textNodes;
28164         days += startingPos;
28165
28166         // convert everything to numbers so it's fast
28167         var day = 86400000;
28168         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28169         var today = new Date().clearTime().getTime();
28170         var sel = date.clearTime().getTime();
28171         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28172         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28173         var ddMatch = this.disabledDatesRE;
28174         var ddText = this.disabledDatesText;
28175         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28176         var ddaysText = this.disabledDaysText;
28177         var format = this.format;
28178
28179         var setCellClass = function(cal, cell){
28180             cell.title = "";
28181             var t = d.getTime();
28182             cell.firstChild.dateValue = t;
28183             if(t == today){
28184                 cell.className += " x-date-today";
28185                 cell.title = cal.todayText;
28186             }
28187             if(t == sel){
28188                 cell.className += " x-date-selected";
28189                 setTimeout(function(){
28190                     try{cell.firstChild.focus();}catch(e){}
28191                 }, 50);
28192             }
28193             // disabling
28194             if(t < min) {
28195                 cell.className = " x-date-disabled";
28196                 cell.title = cal.minText;
28197                 return;
28198             }
28199             if(t > max) {
28200                 cell.className = " x-date-disabled";
28201                 cell.title = cal.maxText;
28202                 return;
28203             }
28204             if(ddays){
28205                 if(ddays.indexOf(d.getDay()) != -1){
28206                     cell.title = ddaysText;
28207                     cell.className = " x-date-disabled";
28208                 }
28209             }
28210             if(ddMatch && format){
28211                 var fvalue = d.dateFormat(format);
28212                 if(ddMatch.test(fvalue)){
28213                     cell.title = ddText.replace("%0", fvalue);
28214                     cell.className = " x-date-disabled";
28215                 }
28216             }
28217         };
28218
28219         var i = 0;
28220         for(; i < startingPos; i++) {
28221             textEls[i].innerHTML = (++prevStart);
28222             d.setDate(d.getDate()+1);
28223             cells[i].className = "x-date-prevday";
28224             setCellClass(this, cells[i]);
28225         }
28226         for(; i < days; i++){
28227             intDay = i - startingPos + 1;
28228             textEls[i].innerHTML = (intDay);
28229             d.setDate(d.getDate()+1);
28230             cells[i].className = "x-date-active";
28231             setCellClass(this, cells[i]);
28232         }
28233         var extraDays = 0;
28234         for(; i < 42; i++) {
28235              textEls[i].innerHTML = (++extraDays);
28236              d.setDate(d.getDate()+1);
28237              cells[i].className = "x-date-nextday";
28238              setCellClass(this, cells[i]);
28239         }
28240
28241         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28242         this.fireEvent('monthchange', this, date);
28243         
28244         if(!this.internalRender){
28245             var main = this.el.dom.firstChild;
28246             var w = main.offsetWidth;
28247             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28248             Roo.fly(main).setWidth(w);
28249             this.internalRender = true;
28250             // opera does not respect the auto grow header center column
28251             // then, after it gets a width opera refuses to recalculate
28252             // without a second pass
28253             if(Roo.isOpera && !this.secondPass){
28254                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28255                 this.secondPass = true;
28256                 this.update.defer(10, this, [date]);
28257             }
28258         }
28259         
28260         
28261     }
28262 });        /*
28263  * Based on:
28264  * Ext JS Library 1.1.1
28265  * Copyright(c) 2006-2007, Ext JS, LLC.
28266  *
28267  * Originally Released Under LGPL - original licence link has changed is not relivant.
28268  *
28269  * Fork - LGPL
28270  * <script type="text/javascript">
28271  */
28272 /**
28273  * @class Roo.TabPanel
28274  * @extends Roo.util.Observable
28275  * A lightweight tab container.
28276  * <br><br>
28277  * Usage:
28278  * <pre><code>
28279 // basic tabs 1, built from existing content
28280 var tabs = new Roo.TabPanel("tabs1");
28281 tabs.addTab("script", "View Script");
28282 tabs.addTab("markup", "View Markup");
28283 tabs.activate("script");
28284
28285 // more advanced tabs, built from javascript
28286 var jtabs = new Roo.TabPanel("jtabs");
28287 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28288
28289 // set up the UpdateManager
28290 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28291 var updater = tab2.getUpdateManager();
28292 updater.setDefaultUrl("ajax1.htm");
28293 tab2.on('activate', updater.refresh, updater, true);
28294
28295 // Use setUrl for Ajax loading
28296 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28297 tab3.setUrl("ajax2.htm", null, true);
28298
28299 // Disabled tab
28300 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28301 tab4.disable();
28302
28303 jtabs.activate("jtabs-1");
28304  * </code></pre>
28305  * @constructor
28306  * Create a new TabPanel.
28307  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28308  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28309  */
28310 Roo.TabPanel = function(container, config){
28311     /**
28312     * The container element for this TabPanel.
28313     * @type Roo.Element
28314     */
28315     this.el = Roo.get(container, true);
28316     if(config){
28317         if(typeof config == "boolean"){
28318             this.tabPosition = config ? "bottom" : "top";
28319         }else{
28320             Roo.apply(this, config);
28321         }
28322     }
28323     if(this.tabPosition == "bottom"){
28324         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28325         this.el.addClass("x-tabs-bottom");
28326     }
28327     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28328     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28329     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28330     if(Roo.isIE){
28331         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28332     }
28333     if(this.tabPosition != "bottom"){
28334         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28335          * @type Roo.Element
28336          */
28337         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28338         this.el.addClass("x-tabs-top");
28339     }
28340     this.items = [];
28341
28342     this.bodyEl.setStyle("position", "relative");
28343
28344     this.active = null;
28345     this.activateDelegate = this.activate.createDelegate(this);
28346
28347     this.addEvents({
28348         /**
28349          * @event tabchange
28350          * Fires when the active tab changes
28351          * @param {Roo.TabPanel} this
28352          * @param {Roo.TabPanelItem} activePanel The new active tab
28353          */
28354         "tabchange": true,
28355         /**
28356          * @event beforetabchange
28357          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28358          * @param {Roo.TabPanel} this
28359          * @param {Object} e Set cancel to true on this object to cancel the tab change
28360          * @param {Roo.TabPanelItem} tab The tab being changed to
28361          */
28362         "beforetabchange" : true
28363     });
28364
28365     Roo.EventManager.onWindowResize(this.onResize, this);
28366     this.cpad = this.el.getPadding("lr");
28367     this.hiddenCount = 0;
28368
28369
28370     // toolbar on the tabbar support...
28371     if (this.toolbar) {
28372         var tcfg = this.toolbar;
28373         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28374         this.toolbar = new Roo.Toolbar(tcfg);
28375         if (Roo.isSafari) {
28376             var tbl = tcfg.container.child('table', true);
28377             tbl.setAttribute('width', '100%');
28378         }
28379         
28380     }
28381    
28382
28383
28384     Roo.TabPanel.superclass.constructor.call(this);
28385 };
28386
28387 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28388     /*
28389      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28390      */
28391     tabPosition : "top",
28392     /*
28393      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28394      */
28395     currentTabWidth : 0,
28396     /*
28397      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28398      */
28399     minTabWidth : 40,
28400     /*
28401      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28402      */
28403     maxTabWidth : 250,
28404     /*
28405      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28406      */
28407     preferredTabWidth : 175,
28408     /*
28409      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28410      */
28411     resizeTabs : false,
28412     /*
28413      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28414      */
28415     monitorResize : true,
28416     /*
28417      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28418      */
28419     toolbar : false,
28420
28421     /**
28422      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28423      * @param {String} id The id of the div to use <b>or create</b>
28424      * @param {String} text The text for the tab
28425      * @param {String} content (optional) Content to put in the TabPanelItem body
28426      * @param {Boolean} closable (optional) True to create a close icon on the tab
28427      * @return {Roo.TabPanelItem} The created TabPanelItem
28428      */
28429     addTab : function(id, text, content, closable){
28430         var item = new Roo.TabPanelItem(this, id, text, closable);
28431         this.addTabItem(item);
28432         if(content){
28433             item.setContent(content);
28434         }
28435         return item;
28436     },
28437
28438     /**
28439      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28440      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28441      * @return {Roo.TabPanelItem}
28442      */
28443     getTab : function(id){
28444         return this.items[id];
28445     },
28446
28447     /**
28448      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28449      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28450      */
28451     hideTab : function(id){
28452         var t = this.items[id];
28453         if(!t.isHidden()){
28454            t.setHidden(true);
28455            this.hiddenCount++;
28456            this.autoSizeTabs();
28457         }
28458     },
28459
28460     /**
28461      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28462      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28463      */
28464     unhideTab : function(id){
28465         var t = this.items[id];
28466         if(t.isHidden()){
28467            t.setHidden(false);
28468            this.hiddenCount--;
28469            this.autoSizeTabs();
28470         }
28471     },
28472
28473     /**
28474      * Adds an existing {@link Roo.TabPanelItem}.
28475      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28476      */
28477     addTabItem : function(item){
28478         this.items[item.id] = item;
28479         this.items.push(item);
28480         if(this.resizeTabs){
28481            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28482            this.autoSizeTabs();
28483         }else{
28484             item.autoSize();
28485         }
28486     },
28487
28488     /**
28489      * Removes a {@link Roo.TabPanelItem}.
28490      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28491      */
28492     removeTab : function(id){
28493         var items = this.items;
28494         var tab = items[id];
28495         if(!tab) { return; }
28496         var index = items.indexOf(tab);
28497         if(this.active == tab && items.length > 1){
28498             var newTab = this.getNextAvailable(index);
28499             if(newTab) {
28500                 newTab.activate();
28501             }
28502         }
28503         this.stripEl.dom.removeChild(tab.pnode.dom);
28504         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28505             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28506         }
28507         items.splice(index, 1);
28508         delete this.items[tab.id];
28509         tab.fireEvent("close", tab);
28510         tab.purgeListeners();
28511         this.autoSizeTabs();
28512     },
28513
28514     getNextAvailable : function(start){
28515         var items = this.items;
28516         var index = start;
28517         // look for a next tab that will slide over to
28518         // replace the one being removed
28519         while(index < items.length){
28520             var item = items[++index];
28521             if(item && !item.isHidden()){
28522                 return item;
28523             }
28524         }
28525         // if one isn't found select the previous tab (on the left)
28526         index = start;
28527         while(index >= 0){
28528             var item = items[--index];
28529             if(item && !item.isHidden()){
28530                 return item;
28531             }
28532         }
28533         return null;
28534     },
28535
28536     /**
28537      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28538      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28539      */
28540     disableTab : function(id){
28541         var tab = this.items[id];
28542         if(tab && this.active != tab){
28543             tab.disable();
28544         }
28545     },
28546
28547     /**
28548      * Enables a {@link Roo.TabPanelItem} that is disabled.
28549      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28550      */
28551     enableTab : function(id){
28552         var tab = this.items[id];
28553         tab.enable();
28554     },
28555
28556     /**
28557      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28558      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28559      * @return {Roo.TabPanelItem} The TabPanelItem.
28560      */
28561     activate : function(id){
28562         var tab = this.items[id];
28563         if(!tab){
28564             return null;
28565         }
28566         if(tab == this.active || tab.disabled){
28567             return tab;
28568         }
28569         var e = {};
28570         this.fireEvent("beforetabchange", this, e, tab);
28571         if(e.cancel !== true && !tab.disabled){
28572             if(this.active){
28573                 this.active.hide();
28574             }
28575             this.active = this.items[id];
28576             this.active.show();
28577             this.fireEvent("tabchange", this, this.active);
28578         }
28579         return tab;
28580     },
28581
28582     /**
28583      * Gets the active {@link Roo.TabPanelItem}.
28584      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28585      */
28586     getActiveTab : function(){
28587         return this.active;
28588     },
28589
28590     /**
28591      * Updates the tab body element to fit the height of the container element
28592      * for overflow scrolling
28593      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28594      */
28595     syncHeight : function(targetHeight){
28596         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28597         var bm = this.bodyEl.getMargins();
28598         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28599         this.bodyEl.setHeight(newHeight);
28600         return newHeight;
28601     },
28602
28603     onResize : function(){
28604         if(this.monitorResize){
28605             this.autoSizeTabs();
28606         }
28607     },
28608
28609     /**
28610      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28611      */
28612     beginUpdate : function(){
28613         this.updating = true;
28614     },
28615
28616     /**
28617      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28618      */
28619     endUpdate : function(){
28620         this.updating = false;
28621         this.autoSizeTabs();
28622     },
28623
28624     /**
28625      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28626      */
28627     autoSizeTabs : function(){
28628         var count = this.items.length;
28629         var vcount = count - this.hiddenCount;
28630         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28631             return;
28632         }
28633         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28634         var availWidth = Math.floor(w / vcount);
28635         var b = this.stripBody;
28636         if(b.getWidth() > w){
28637             var tabs = this.items;
28638             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28639             if(availWidth < this.minTabWidth){
28640                 /*if(!this.sleft){    // incomplete scrolling code
28641                     this.createScrollButtons();
28642                 }
28643                 this.showScroll();
28644                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28645             }
28646         }else{
28647             if(this.currentTabWidth < this.preferredTabWidth){
28648                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28649             }
28650         }
28651     },
28652
28653     /**
28654      * Returns the number of tabs in this TabPanel.
28655      * @return {Number}
28656      */
28657      getCount : function(){
28658          return this.items.length;
28659      },
28660
28661     /**
28662      * Resizes all the tabs to the passed width
28663      * @param {Number} The new width
28664      */
28665     setTabWidth : function(width){
28666         this.currentTabWidth = width;
28667         for(var i = 0, len = this.items.length; i < len; i++) {
28668                 if(!this.items[i].isHidden()) {
28669                 this.items[i].setWidth(width);
28670             }
28671         }
28672     },
28673
28674     /**
28675      * Destroys this TabPanel
28676      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28677      */
28678     destroy : function(removeEl){
28679         Roo.EventManager.removeResizeListener(this.onResize, this);
28680         for(var i = 0, len = this.items.length; i < len; i++){
28681             this.items[i].purgeListeners();
28682         }
28683         if(removeEl === true){
28684             this.el.update("");
28685             this.el.remove();
28686         }
28687     }
28688 });
28689
28690 /**
28691  * @class Roo.TabPanelItem
28692  * @extends Roo.util.Observable
28693  * Represents an individual item (tab plus body) in a TabPanel.
28694  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28695  * @param {String} id The id of this TabPanelItem
28696  * @param {String} text The text for the tab of this TabPanelItem
28697  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28698  */
28699 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28700     /**
28701      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28702      * @type Roo.TabPanel
28703      */
28704     this.tabPanel = tabPanel;
28705     /**
28706      * The id for this TabPanelItem
28707      * @type String
28708      */
28709     this.id = id;
28710     /** @private */
28711     this.disabled = false;
28712     /** @private */
28713     this.text = text;
28714     /** @private */
28715     this.loaded = false;
28716     this.closable = closable;
28717
28718     /**
28719      * The body element for this TabPanelItem.
28720      * @type Roo.Element
28721      */
28722     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28723     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28724     this.bodyEl.setStyle("display", "block");
28725     this.bodyEl.setStyle("zoom", "1");
28726     this.hideAction();
28727
28728     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28729     /** @private */
28730     this.el = Roo.get(els.el, true);
28731     this.inner = Roo.get(els.inner, true);
28732     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28733     this.pnode = Roo.get(els.el.parentNode, true);
28734     this.el.on("mousedown", this.onTabMouseDown, this);
28735     this.el.on("click", this.onTabClick, this);
28736     /** @private */
28737     if(closable){
28738         var c = Roo.get(els.close, true);
28739         c.dom.title = this.closeText;
28740         c.addClassOnOver("close-over");
28741         c.on("click", this.closeClick, this);
28742      }
28743
28744     this.addEvents({
28745          /**
28746          * @event activate
28747          * Fires when this tab becomes the active tab.
28748          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28749          * @param {Roo.TabPanelItem} this
28750          */
28751         "activate": true,
28752         /**
28753          * @event beforeclose
28754          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28755          * @param {Roo.TabPanelItem} this
28756          * @param {Object} e Set cancel to true on this object to cancel the close.
28757          */
28758         "beforeclose": true,
28759         /**
28760          * @event close
28761          * Fires when this tab is closed.
28762          * @param {Roo.TabPanelItem} this
28763          */
28764          "close": true,
28765         /**
28766          * @event deactivate
28767          * Fires when this tab is no longer the active tab.
28768          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28769          * @param {Roo.TabPanelItem} this
28770          */
28771          "deactivate" : true
28772     });
28773     this.hidden = false;
28774
28775     Roo.TabPanelItem.superclass.constructor.call(this);
28776 };
28777
28778 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28779     purgeListeners : function(){
28780        Roo.util.Observable.prototype.purgeListeners.call(this);
28781        this.el.removeAllListeners();
28782     },
28783     /**
28784      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28785      */
28786     show : function(){
28787         this.pnode.addClass("on");
28788         this.showAction();
28789         if(Roo.isOpera){
28790             this.tabPanel.stripWrap.repaint();
28791         }
28792         this.fireEvent("activate", this.tabPanel, this);
28793     },
28794
28795     /**
28796      * Returns true if this tab is the active tab.
28797      * @return {Boolean}
28798      */
28799     isActive : function(){
28800         return this.tabPanel.getActiveTab() == this;
28801     },
28802
28803     /**
28804      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28805      */
28806     hide : function(){
28807         this.pnode.removeClass("on");
28808         this.hideAction();
28809         this.fireEvent("deactivate", this.tabPanel, this);
28810     },
28811
28812     hideAction : function(){
28813         this.bodyEl.hide();
28814         this.bodyEl.setStyle("position", "absolute");
28815         this.bodyEl.setLeft("-20000px");
28816         this.bodyEl.setTop("-20000px");
28817     },
28818
28819     showAction : function(){
28820         this.bodyEl.setStyle("position", "relative");
28821         this.bodyEl.setTop("");
28822         this.bodyEl.setLeft("");
28823         this.bodyEl.show();
28824     },
28825
28826     /**
28827      * Set the tooltip for the tab.
28828      * @param {String} tooltip The tab's tooltip
28829      */
28830     setTooltip : function(text){
28831         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
28832             this.textEl.dom.qtip = text;
28833             this.textEl.dom.removeAttribute('title');
28834         }else{
28835             this.textEl.dom.title = text;
28836         }
28837     },
28838
28839     onTabClick : function(e){
28840         e.preventDefault();
28841         this.tabPanel.activate(this.id);
28842     },
28843
28844     onTabMouseDown : function(e){
28845         e.preventDefault();
28846         this.tabPanel.activate(this.id);
28847     },
28848
28849     getWidth : function(){
28850         return this.inner.getWidth();
28851     },
28852
28853     setWidth : function(width){
28854         var iwidth = width - this.pnode.getPadding("lr");
28855         this.inner.setWidth(iwidth);
28856         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
28857         this.pnode.setWidth(width);
28858     },
28859
28860     /**
28861      * Show or hide the tab
28862      * @param {Boolean} hidden True to hide or false to show.
28863      */
28864     setHidden : function(hidden){
28865         this.hidden = hidden;
28866         this.pnode.setStyle("display", hidden ? "none" : "");
28867     },
28868
28869     /**
28870      * Returns true if this tab is "hidden"
28871      * @return {Boolean}
28872      */
28873     isHidden : function(){
28874         return this.hidden;
28875     },
28876
28877     /**
28878      * Returns the text for this tab
28879      * @return {String}
28880      */
28881     getText : function(){
28882         return this.text;
28883     },
28884
28885     autoSize : function(){
28886         //this.el.beginMeasure();
28887         this.textEl.setWidth(1);
28888         /*
28889          *  #2804 [new] Tabs in Roojs
28890          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
28891          */
28892         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
28893         //this.el.endMeasure();
28894     },
28895
28896     /**
28897      * Sets the text for the tab (Note: this also sets the tooltip text)
28898      * @param {String} text The tab's text and tooltip
28899      */
28900     setText : function(text){
28901         this.text = text;
28902         this.textEl.update(text);
28903         this.setTooltip(text);
28904         if(!this.tabPanel.resizeTabs){
28905             this.autoSize();
28906         }
28907     },
28908     /**
28909      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
28910      */
28911     activate : function(){
28912         this.tabPanel.activate(this.id);
28913     },
28914
28915     /**
28916      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
28917      */
28918     disable : function(){
28919         if(this.tabPanel.active != this){
28920             this.disabled = true;
28921             this.pnode.addClass("disabled");
28922         }
28923     },
28924
28925     /**
28926      * Enables this TabPanelItem if it was previously disabled.
28927      */
28928     enable : function(){
28929         this.disabled = false;
28930         this.pnode.removeClass("disabled");
28931     },
28932
28933     /**
28934      * Sets the content for this TabPanelItem.
28935      * @param {String} content The content
28936      * @param {Boolean} loadScripts true to look for and load scripts
28937      */
28938     setContent : function(content, loadScripts){
28939         this.bodyEl.update(content, loadScripts);
28940     },
28941
28942     /**
28943      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
28944      * @return {Roo.UpdateManager} The UpdateManager
28945      */
28946     getUpdateManager : function(){
28947         return this.bodyEl.getUpdateManager();
28948     },
28949
28950     /**
28951      * Set a URL to be used to load the content for this TabPanelItem.
28952      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
28953      * @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)
28954      * @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)
28955      * @return {Roo.UpdateManager} The UpdateManager
28956      */
28957     setUrl : function(url, params, loadOnce){
28958         if(this.refreshDelegate){
28959             this.un('activate', this.refreshDelegate);
28960         }
28961         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
28962         this.on("activate", this.refreshDelegate);
28963         return this.bodyEl.getUpdateManager();
28964     },
28965
28966     /** @private */
28967     _handleRefresh : function(url, params, loadOnce){
28968         if(!loadOnce || !this.loaded){
28969             var updater = this.bodyEl.getUpdateManager();
28970             updater.update(url, params, this._setLoaded.createDelegate(this));
28971         }
28972     },
28973
28974     /**
28975      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
28976      *   Will fail silently if the setUrl method has not been called.
28977      *   This does not activate the panel, just updates its content.
28978      */
28979     refresh : function(){
28980         if(this.refreshDelegate){
28981            this.loaded = false;
28982            this.refreshDelegate();
28983         }
28984     },
28985
28986     /** @private */
28987     _setLoaded : function(){
28988         this.loaded = true;
28989     },
28990
28991     /** @private */
28992     closeClick : function(e){
28993         var o = {};
28994         e.stopEvent();
28995         this.fireEvent("beforeclose", this, o);
28996         if(o.cancel !== true){
28997             this.tabPanel.removeTab(this.id);
28998         }
28999     },
29000     /**
29001      * The text displayed in the tooltip for the close icon.
29002      * @type String
29003      */
29004     closeText : "Close this tab"
29005 });
29006
29007 /** @private */
29008 Roo.TabPanel.prototype.createStrip = function(container){
29009     var strip = document.createElement("div");
29010     strip.className = "x-tabs-wrap";
29011     container.appendChild(strip);
29012     return strip;
29013 };
29014 /** @private */
29015 Roo.TabPanel.prototype.createStripList = function(strip){
29016     // div wrapper for retard IE
29017     // returns the "tr" element.
29018     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
29019         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
29020         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
29021     return strip.firstChild.firstChild.firstChild.firstChild;
29022 };
29023 /** @private */
29024 Roo.TabPanel.prototype.createBody = function(container){
29025     var body = document.createElement("div");
29026     Roo.id(body, "tab-body");
29027     Roo.fly(body).addClass("x-tabs-body");
29028     container.appendChild(body);
29029     return body;
29030 };
29031 /** @private */
29032 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
29033     var body = Roo.getDom(id);
29034     if(!body){
29035         body = document.createElement("div");
29036         body.id = id;
29037     }
29038     Roo.fly(body).addClass("x-tabs-item-body");
29039     bodyEl.insertBefore(body, bodyEl.firstChild);
29040     return body;
29041 };
29042 /** @private */
29043 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
29044     var td = document.createElement("td");
29045     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
29046     //stripEl.appendChild(td);
29047     if(closable){
29048         td.className = "x-tabs-closable";
29049         if(!this.closeTpl){
29050             this.closeTpl = new Roo.Template(
29051                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29052                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
29053                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
29054             );
29055         }
29056         var el = this.closeTpl.overwrite(td, {"text": text});
29057         var close = el.getElementsByTagName("div")[0];
29058         var inner = el.getElementsByTagName("em")[0];
29059         return {"el": el, "close": close, "inner": inner};
29060     } else {
29061         if(!this.tabTpl){
29062             this.tabTpl = new Roo.Template(
29063                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29064                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
29065             );
29066         }
29067         var el = this.tabTpl.overwrite(td, {"text": text});
29068         var inner = el.getElementsByTagName("em")[0];
29069         return {"el": el, "inner": inner};
29070     }
29071 };/*
29072  * Based on:
29073  * Ext JS Library 1.1.1
29074  * Copyright(c) 2006-2007, Ext JS, LLC.
29075  *
29076  * Originally Released Under LGPL - original licence link has changed is not relivant.
29077  *
29078  * Fork - LGPL
29079  * <script type="text/javascript">
29080  */
29081
29082 /**
29083  * @class Roo.Button
29084  * @extends Roo.util.Observable
29085  * Simple Button class
29086  * @cfg {String} text The button text
29087  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29088  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29089  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29090  * @cfg {Object} scope The scope of the handler
29091  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29092  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29093  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29094  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29095  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29096  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29097    applies if enableToggle = true)
29098  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29099  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29100   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29101  * @constructor
29102  * Create a new button
29103  * @param {Object} config The config object
29104  */
29105 Roo.Button = function(renderTo, config)
29106 {
29107     if (!config) {
29108         config = renderTo;
29109         renderTo = config.renderTo || false;
29110     }
29111     
29112     Roo.apply(this, config);
29113     this.addEvents({
29114         /**
29115              * @event click
29116              * Fires when this button is clicked
29117              * @param {Button} this
29118              * @param {EventObject} e The click event
29119              */
29120             "click" : true,
29121         /**
29122              * @event toggle
29123              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29124              * @param {Button} this
29125              * @param {Boolean} pressed
29126              */
29127             "toggle" : true,
29128         /**
29129              * @event mouseover
29130              * Fires when the mouse hovers over the button
29131              * @param {Button} this
29132              * @param {Event} e The event object
29133              */
29134         'mouseover' : true,
29135         /**
29136              * @event mouseout
29137              * Fires when the mouse exits the button
29138              * @param {Button} this
29139              * @param {Event} e The event object
29140              */
29141         'mouseout': true,
29142          /**
29143              * @event render
29144              * Fires when the button is rendered
29145              * @param {Button} this
29146              */
29147         'render': true
29148     });
29149     if(this.menu){
29150         this.menu = Roo.menu.MenuMgr.get(this.menu);
29151     }
29152     // register listeners first!!  - so render can be captured..
29153     Roo.util.Observable.call(this);
29154     if(renderTo){
29155         this.render(renderTo);
29156     }
29157     
29158   
29159 };
29160
29161 Roo.extend(Roo.Button, Roo.util.Observable, {
29162     /**
29163      * 
29164      */
29165     
29166     /**
29167      * Read-only. True if this button is hidden
29168      * @type Boolean
29169      */
29170     hidden : false,
29171     /**
29172      * Read-only. True if this button is disabled
29173      * @type Boolean
29174      */
29175     disabled : false,
29176     /**
29177      * Read-only. True if this button is pressed (only if enableToggle = true)
29178      * @type Boolean
29179      */
29180     pressed : false,
29181
29182     /**
29183      * @cfg {Number} tabIndex 
29184      * The DOM tabIndex for this button (defaults to undefined)
29185      */
29186     tabIndex : undefined,
29187
29188     /**
29189      * @cfg {Boolean} enableToggle
29190      * True to enable pressed/not pressed toggling (defaults to false)
29191      */
29192     enableToggle: false,
29193     /**
29194      * @cfg {Mixed} menu
29195      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29196      */
29197     menu : undefined,
29198     /**
29199      * @cfg {String} menuAlign
29200      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29201      */
29202     menuAlign : "tl-bl?",
29203
29204     /**
29205      * @cfg {String} iconCls
29206      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29207      */
29208     iconCls : undefined,
29209     /**
29210      * @cfg {String} type
29211      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29212      */
29213     type : 'button',
29214
29215     // private
29216     menuClassTarget: 'tr',
29217
29218     /**
29219      * @cfg {String} clickEvent
29220      * The type of event to map to the button's event handler (defaults to 'click')
29221      */
29222     clickEvent : 'click',
29223
29224     /**
29225      * @cfg {Boolean} handleMouseEvents
29226      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29227      */
29228     handleMouseEvents : true,
29229
29230     /**
29231      * @cfg {String} tooltipType
29232      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29233      */
29234     tooltipType : 'qtip',
29235
29236     /**
29237      * @cfg {String} cls
29238      * A CSS class to apply to the button's main element.
29239      */
29240     
29241     /**
29242      * @cfg {Roo.Template} template (Optional)
29243      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29244      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29245      * require code modifications if required elements (e.g. a button) aren't present.
29246      */
29247
29248     // private
29249     render : function(renderTo){
29250         var btn;
29251         if(this.hideParent){
29252             this.parentEl = Roo.get(renderTo);
29253         }
29254         if(!this.dhconfig){
29255             if(!this.template){
29256                 if(!Roo.Button.buttonTemplate){
29257                     // hideous table template
29258                     Roo.Button.buttonTemplate = new Roo.Template(
29259                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29260                         '<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>',
29261                         "</tr></tbody></table>");
29262                 }
29263                 this.template = Roo.Button.buttonTemplate;
29264             }
29265             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29266             var btnEl = btn.child("button:first");
29267             btnEl.on('focus', this.onFocus, this);
29268             btnEl.on('blur', this.onBlur, this);
29269             if(this.cls){
29270                 btn.addClass(this.cls);
29271             }
29272             if(this.icon){
29273                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29274             }
29275             if(this.iconCls){
29276                 btnEl.addClass(this.iconCls);
29277                 if(!this.cls){
29278                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29279                 }
29280             }
29281             if(this.tabIndex !== undefined){
29282                 btnEl.dom.tabIndex = this.tabIndex;
29283             }
29284             if(this.tooltip){
29285                 if(typeof this.tooltip == 'object'){
29286                     Roo.QuickTips.tips(Roo.apply({
29287                           target: btnEl.id
29288                     }, this.tooltip));
29289                 } else {
29290                     btnEl.dom[this.tooltipType] = this.tooltip;
29291                 }
29292             }
29293         }else{
29294             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29295         }
29296         this.el = btn;
29297         if(this.id){
29298             this.el.dom.id = this.el.id = this.id;
29299         }
29300         if(this.menu){
29301             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29302             this.menu.on("show", this.onMenuShow, this);
29303             this.menu.on("hide", this.onMenuHide, this);
29304         }
29305         btn.addClass("x-btn");
29306         if(Roo.isIE && !Roo.isIE7){
29307             this.autoWidth.defer(1, this);
29308         }else{
29309             this.autoWidth();
29310         }
29311         if(this.handleMouseEvents){
29312             btn.on("mouseover", this.onMouseOver, this);
29313             btn.on("mouseout", this.onMouseOut, this);
29314             btn.on("mousedown", this.onMouseDown, this);
29315         }
29316         btn.on(this.clickEvent, this.onClick, this);
29317         //btn.on("mouseup", this.onMouseUp, this);
29318         if(this.hidden){
29319             this.hide();
29320         }
29321         if(this.disabled){
29322             this.disable();
29323         }
29324         Roo.ButtonToggleMgr.register(this);
29325         if(this.pressed){
29326             this.el.addClass("x-btn-pressed");
29327         }
29328         if(this.repeat){
29329             var repeater = new Roo.util.ClickRepeater(btn,
29330                 typeof this.repeat == "object" ? this.repeat : {}
29331             );
29332             repeater.on("click", this.onClick,  this);
29333         }
29334         
29335         this.fireEvent('render', this);
29336         
29337     },
29338     /**
29339      * Returns the button's underlying element
29340      * @return {Roo.Element} The element
29341      */
29342     getEl : function(){
29343         return this.el;  
29344     },
29345     
29346     /**
29347      * Destroys this Button and removes any listeners.
29348      */
29349     destroy : function(){
29350         Roo.ButtonToggleMgr.unregister(this);
29351         this.el.removeAllListeners();
29352         this.purgeListeners();
29353         this.el.remove();
29354     },
29355
29356     // private
29357     autoWidth : function(){
29358         if(this.el){
29359             this.el.setWidth("auto");
29360             if(Roo.isIE7 && Roo.isStrict){
29361                 var ib = this.el.child('button');
29362                 if(ib && ib.getWidth() > 20){
29363                     ib.clip();
29364                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29365                 }
29366             }
29367             if(this.minWidth){
29368                 if(this.hidden){
29369                     this.el.beginMeasure();
29370                 }
29371                 if(this.el.getWidth() < this.minWidth){
29372                     this.el.setWidth(this.minWidth);
29373                 }
29374                 if(this.hidden){
29375                     this.el.endMeasure();
29376                 }
29377             }
29378         }
29379     },
29380
29381     /**
29382      * Assigns this button's click handler
29383      * @param {Function} handler The function to call when the button is clicked
29384      * @param {Object} scope (optional) Scope for the function passed in
29385      */
29386     setHandler : function(handler, scope){
29387         this.handler = handler;
29388         this.scope = scope;  
29389     },
29390     
29391     /**
29392      * Sets this button's text
29393      * @param {String} text The button text
29394      */
29395     setText : function(text){
29396         this.text = text;
29397         if(this.el){
29398             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29399         }
29400         this.autoWidth();
29401     },
29402     
29403     /**
29404      * Gets the text for this button
29405      * @return {String} The button text
29406      */
29407     getText : function(){
29408         return this.text;  
29409     },
29410     
29411     /**
29412      * Show this button
29413      */
29414     show: function(){
29415         this.hidden = false;
29416         if(this.el){
29417             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29418         }
29419     },
29420     
29421     /**
29422      * Hide this button
29423      */
29424     hide: function(){
29425         this.hidden = true;
29426         if(this.el){
29427             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29428         }
29429     },
29430     
29431     /**
29432      * Convenience function for boolean show/hide
29433      * @param {Boolean} visible True to show, false to hide
29434      */
29435     setVisible: function(visible){
29436         if(visible) {
29437             this.show();
29438         }else{
29439             this.hide();
29440         }
29441     },
29442     
29443     /**
29444      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29445      * @param {Boolean} state (optional) Force a particular state
29446      */
29447     toggle : function(state){
29448         state = state === undefined ? !this.pressed : state;
29449         if(state != this.pressed){
29450             if(state){
29451                 this.el.addClass("x-btn-pressed");
29452                 this.pressed = true;
29453                 this.fireEvent("toggle", this, true);
29454             }else{
29455                 this.el.removeClass("x-btn-pressed");
29456                 this.pressed = false;
29457                 this.fireEvent("toggle", this, false);
29458             }
29459             if(this.toggleHandler){
29460                 this.toggleHandler.call(this.scope || this, this, state);
29461             }
29462         }
29463     },
29464     
29465     /**
29466      * Focus the button
29467      */
29468     focus : function(){
29469         this.el.child('button:first').focus();
29470     },
29471     
29472     /**
29473      * Disable this button
29474      */
29475     disable : function(){
29476         if(this.el){
29477             this.el.addClass("x-btn-disabled");
29478         }
29479         this.disabled = true;
29480     },
29481     
29482     /**
29483      * Enable this button
29484      */
29485     enable : function(){
29486         if(this.el){
29487             this.el.removeClass("x-btn-disabled");
29488         }
29489         this.disabled = false;
29490     },
29491
29492     /**
29493      * Convenience function for boolean enable/disable
29494      * @param {Boolean} enabled True to enable, false to disable
29495      */
29496     setDisabled : function(v){
29497         this[v !== true ? "enable" : "disable"]();
29498     },
29499
29500     // private
29501     onClick : function(e)
29502     {
29503         if(e){
29504             e.preventDefault();
29505         }
29506         if(e.button != 0){
29507             return;
29508         }
29509         if(!this.disabled){
29510             if(this.enableToggle){
29511                 this.toggle();
29512             }
29513             if(this.menu && !this.menu.isVisible()){
29514                 this.menu.show(this.el, this.menuAlign);
29515             }
29516             this.fireEvent("click", this, e);
29517             if(this.handler){
29518                 this.el.removeClass("x-btn-over");
29519                 this.handler.call(this.scope || this, this, e);
29520             }
29521         }
29522     },
29523     // private
29524     onMouseOver : function(e){
29525         if(!this.disabled){
29526             this.el.addClass("x-btn-over");
29527             this.fireEvent('mouseover', this, e);
29528         }
29529     },
29530     // private
29531     onMouseOut : function(e){
29532         if(!e.within(this.el,  true)){
29533             this.el.removeClass("x-btn-over");
29534             this.fireEvent('mouseout', this, e);
29535         }
29536     },
29537     // private
29538     onFocus : function(e){
29539         if(!this.disabled){
29540             this.el.addClass("x-btn-focus");
29541         }
29542     },
29543     // private
29544     onBlur : function(e){
29545         this.el.removeClass("x-btn-focus");
29546     },
29547     // private
29548     onMouseDown : function(e){
29549         if(!this.disabled && e.button == 0){
29550             this.el.addClass("x-btn-click");
29551             Roo.get(document).on('mouseup', this.onMouseUp, this);
29552         }
29553     },
29554     // private
29555     onMouseUp : function(e){
29556         if(e.button == 0){
29557             this.el.removeClass("x-btn-click");
29558             Roo.get(document).un('mouseup', this.onMouseUp, this);
29559         }
29560     },
29561     // private
29562     onMenuShow : function(e){
29563         this.el.addClass("x-btn-menu-active");
29564     },
29565     // private
29566     onMenuHide : function(e){
29567         this.el.removeClass("x-btn-menu-active");
29568     }   
29569 });
29570
29571 // Private utility class used by Button
29572 Roo.ButtonToggleMgr = function(){
29573    var groups = {};
29574    
29575    function toggleGroup(btn, state){
29576        if(state){
29577            var g = groups[btn.toggleGroup];
29578            for(var i = 0, l = g.length; i < l; i++){
29579                if(g[i] != btn){
29580                    g[i].toggle(false);
29581                }
29582            }
29583        }
29584    }
29585    
29586    return {
29587        register : function(btn){
29588            if(!btn.toggleGroup){
29589                return;
29590            }
29591            var g = groups[btn.toggleGroup];
29592            if(!g){
29593                g = groups[btn.toggleGroup] = [];
29594            }
29595            g.push(btn);
29596            btn.on("toggle", toggleGroup);
29597        },
29598        
29599        unregister : function(btn){
29600            if(!btn.toggleGroup){
29601                return;
29602            }
29603            var g = groups[btn.toggleGroup];
29604            if(g){
29605                g.remove(btn);
29606                btn.un("toggle", toggleGroup);
29607            }
29608        }
29609    };
29610 }();/*
29611  * Based on:
29612  * Ext JS Library 1.1.1
29613  * Copyright(c) 2006-2007, Ext JS, LLC.
29614  *
29615  * Originally Released Under LGPL - original licence link has changed is not relivant.
29616  *
29617  * Fork - LGPL
29618  * <script type="text/javascript">
29619  */
29620  
29621 /**
29622  * @class Roo.SplitButton
29623  * @extends Roo.Button
29624  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29625  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29626  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29627  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29628  * @cfg {String} arrowTooltip The title attribute of the arrow
29629  * @constructor
29630  * Create a new menu button
29631  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29632  * @param {Object} config The config object
29633  */
29634 Roo.SplitButton = function(renderTo, config){
29635     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29636     /**
29637      * @event arrowclick
29638      * Fires when this button's arrow is clicked
29639      * @param {SplitButton} this
29640      * @param {EventObject} e The click event
29641      */
29642     this.addEvents({"arrowclick":true});
29643 };
29644
29645 Roo.extend(Roo.SplitButton, Roo.Button, {
29646     render : function(renderTo){
29647         // this is one sweet looking template!
29648         var tpl = new Roo.Template(
29649             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29650             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29651             '<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>',
29652             "</tbody></table></td><td>",
29653             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29654             '<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>',
29655             "</tbody></table></td></tr></table>"
29656         );
29657         var btn = tpl.append(renderTo, [this.text, this.type], true);
29658         var btnEl = btn.child("button");
29659         if(this.cls){
29660             btn.addClass(this.cls);
29661         }
29662         if(this.icon){
29663             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29664         }
29665         if(this.iconCls){
29666             btnEl.addClass(this.iconCls);
29667             if(!this.cls){
29668                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29669             }
29670         }
29671         this.el = btn;
29672         if(this.handleMouseEvents){
29673             btn.on("mouseover", this.onMouseOver, this);
29674             btn.on("mouseout", this.onMouseOut, this);
29675             btn.on("mousedown", this.onMouseDown, this);
29676             btn.on("mouseup", this.onMouseUp, this);
29677         }
29678         btn.on(this.clickEvent, this.onClick, this);
29679         if(this.tooltip){
29680             if(typeof this.tooltip == 'object'){
29681                 Roo.QuickTips.tips(Roo.apply({
29682                       target: btnEl.id
29683                 }, this.tooltip));
29684             } else {
29685                 btnEl.dom[this.tooltipType] = this.tooltip;
29686             }
29687         }
29688         if(this.arrowTooltip){
29689             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29690         }
29691         if(this.hidden){
29692             this.hide();
29693         }
29694         if(this.disabled){
29695             this.disable();
29696         }
29697         if(this.pressed){
29698             this.el.addClass("x-btn-pressed");
29699         }
29700         if(Roo.isIE && !Roo.isIE7){
29701             this.autoWidth.defer(1, this);
29702         }else{
29703             this.autoWidth();
29704         }
29705         if(this.menu){
29706             this.menu.on("show", this.onMenuShow, this);
29707             this.menu.on("hide", this.onMenuHide, this);
29708         }
29709         this.fireEvent('render', this);
29710     },
29711
29712     // private
29713     autoWidth : function(){
29714         if(this.el){
29715             var tbl = this.el.child("table:first");
29716             var tbl2 = this.el.child("table:last");
29717             this.el.setWidth("auto");
29718             tbl.setWidth("auto");
29719             if(Roo.isIE7 && Roo.isStrict){
29720                 var ib = this.el.child('button:first');
29721                 if(ib && ib.getWidth() > 20){
29722                     ib.clip();
29723                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29724                 }
29725             }
29726             if(this.minWidth){
29727                 if(this.hidden){
29728                     this.el.beginMeasure();
29729                 }
29730                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29731                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29732                 }
29733                 if(this.hidden){
29734                     this.el.endMeasure();
29735                 }
29736             }
29737             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29738         } 
29739     },
29740     /**
29741      * Sets this button's click handler
29742      * @param {Function} handler The function to call when the button is clicked
29743      * @param {Object} scope (optional) Scope for the function passed above
29744      */
29745     setHandler : function(handler, scope){
29746         this.handler = handler;
29747         this.scope = scope;  
29748     },
29749     
29750     /**
29751      * Sets this button's arrow click handler
29752      * @param {Function} handler The function to call when the arrow is clicked
29753      * @param {Object} scope (optional) Scope for the function passed above
29754      */
29755     setArrowHandler : function(handler, scope){
29756         this.arrowHandler = handler;
29757         this.scope = scope;  
29758     },
29759     
29760     /**
29761      * Focus the button
29762      */
29763     focus : function(){
29764         if(this.el){
29765             this.el.child("button:first").focus();
29766         }
29767     },
29768
29769     // private
29770     onClick : function(e){
29771         e.preventDefault();
29772         if(!this.disabled){
29773             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29774                 if(this.menu && !this.menu.isVisible()){
29775                     this.menu.show(this.el, this.menuAlign);
29776                 }
29777                 this.fireEvent("arrowclick", this, e);
29778                 if(this.arrowHandler){
29779                     this.arrowHandler.call(this.scope || this, this, e);
29780                 }
29781             }else{
29782                 this.fireEvent("click", this, e);
29783                 if(this.handler){
29784                     this.handler.call(this.scope || this, this, e);
29785                 }
29786             }
29787         }
29788     },
29789     // private
29790     onMouseDown : function(e){
29791         if(!this.disabled){
29792             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29793         }
29794     },
29795     // private
29796     onMouseUp : function(e){
29797         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29798     }   
29799 });
29800
29801
29802 // backwards compat
29803 Roo.MenuButton = Roo.SplitButton;/*
29804  * Based on:
29805  * Ext JS Library 1.1.1
29806  * Copyright(c) 2006-2007, Ext JS, LLC.
29807  *
29808  * Originally Released Under LGPL - original licence link has changed is not relivant.
29809  *
29810  * Fork - LGPL
29811  * <script type="text/javascript">
29812  */
29813
29814 /**
29815  * @class Roo.Toolbar
29816  * Basic Toolbar class.
29817  * @constructor
29818  * Creates a new Toolbar
29819  * @param {Object} container The config object
29820  */ 
29821 Roo.Toolbar = function(container, buttons, config)
29822 {
29823     /// old consturctor format still supported..
29824     if(container instanceof Array){ // omit the container for later rendering
29825         buttons = container;
29826         config = buttons;
29827         container = null;
29828     }
29829     if (typeof(container) == 'object' && container.xtype) {
29830         config = container;
29831         container = config.container;
29832         buttons = config.buttons || []; // not really - use items!!
29833     }
29834     var xitems = [];
29835     if (config && config.items) {
29836         xitems = config.items;
29837         delete config.items;
29838     }
29839     Roo.apply(this, config);
29840     this.buttons = buttons;
29841     
29842     if(container){
29843         this.render(container);
29844     }
29845     this.xitems = xitems;
29846     Roo.each(xitems, function(b) {
29847         this.add(b);
29848     }, this);
29849     
29850 };
29851
29852 Roo.Toolbar.prototype = {
29853     /**
29854      * @cfg {Array} items
29855      * array of button configs or elements to add (will be converted to a MixedCollection)
29856      */
29857     
29858     /**
29859      * @cfg {String/HTMLElement/Element} container
29860      * The id or element that will contain the toolbar
29861      */
29862     // private
29863     render : function(ct){
29864         this.el = Roo.get(ct);
29865         if(this.cls){
29866             this.el.addClass(this.cls);
29867         }
29868         // using a table allows for vertical alignment
29869         // 100% width is needed by Safari...
29870         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
29871         this.tr = this.el.child("tr", true);
29872         var autoId = 0;
29873         this.items = new Roo.util.MixedCollection(false, function(o){
29874             return o.id || ("item" + (++autoId));
29875         });
29876         if(this.buttons){
29877             this.add.apply(this, this.buttons);
29878             delete this.buttons;
29879         }
29880     },
29881
29882     /**
29883      * Adds element(s) to the toolbar -- this function takes a variable number of 
29884      * arguments of mixed type and adds them to the toolbar.
29885      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
29886      * <ul>
29887      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
29888      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
29889      * <li>Field: Any form field (equivalent to {@link #addField})</li>
29890      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
29891      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
29892      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
29893      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
29894      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
29895      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
29896      * </ul>
29897      * @param {Mixed} arg2
29898      * @param {Mixed} etc.
29899      */
29900     add : function(){
29901         var a = arguments, l = a.length;
29902         for(var i = 0; i < l; i++){
29903             this._add(a[i]);
29904         }
29905     },
29906     // private..
29907     _add : function(el) {
29908         
29909         if (el.xtype) {
29910             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
29911         }
29912         
29913         if (el.applyTo){ // some kind of form field
29914             return this.addField(el);
29915         } 
29916         if (el.render){ // some kind of Toolbar.Item
29917             return this.addItem(el);
29918         }
29919         if (typeof el == "string"){ // string
29920             if(el == "separator" || el == "-"){
29921                 return this.addSeparator();
29922             }
29923             if (el == " "){
29924                 return this.addSpacer();
29925             }
29926             if(el == "->"){
29927                 return this.addFill();
29928             }
29929             return this.addText(el);
29930             
29931         }
29932         if(el.tagName){ // element
29933             return this.addElement(el);
29934         }
29935         if(typeof el == "object"){ // must be button config?
29936             return this.addButton(el);
29937         }
29938         // and now what?!?!
29939         return false;
29940         
29941     },
29942     
29943     /**
29944      * Add an Xtype element
29945      * @param {Object} xtype Xtype Object
29946      * @return {Object} created Object
29947      */
29948     addxtype : function(e){
29949         return this.add(e);  
29950     },
29951     
29952     /**
29953      * Returns the Element for this toolbar.
29954      * @return {Roo.Element}
29955      */
29956     getEl : function(){
29957         return this.el;  
29958     },
29959     
29960     /**
29961      * Adds a separator
29962      * @return {Roo.Toolbar.Item} The separator item
29963      */
29964     addSeparator : function(){
29965         return this.addItem(new Roo.Toolbar.Separator());
29966     },
29967
29968     /**
29969      * Adds a spacer element
29970      * @return {Roo.Toolbar.Spacer} The spacer item
29971      */
29972     addSpacer : function(){
29973         return this.addItem(new Roo.Toolbar.Spacer());
29974     },
29975
29976     /**
29977      * Adds a fill element that forces subsequent additions to the right side of the toolbar
29978      * @return {Roo.Toolbar.Fill} The fill item
29979      */
29980     addFill : function(){
29981         return this.addItem(new Roo.Toolbar.Fill());
29982     },
29983
29984     /**
29985      * Adds any standard HTML element to the toolbar
29986      * @param {String/HTMLElement/Element} el The element or id of the element to add
29987      * @return {Roo.Toolbar.Item} The element's item
29988      */
29989     addElement : function(el){
29990         return this.addItem(new Roo.Toolbar.Item(el));
29991     },
29992     /**
29993      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
29994      * @type Roo.util.MixedCollection  
29995      */
29996     items : false,
29997      
29998     /**
29999      * Adds any Toolbar.Item or subclass
30000      * @param {Roo.Toolbar.Item} item
30001      * @return {Roo.Toolbar.Item} The item
30002      */
30003     addItem : function(item){
30004         var td = this.nextBlock();
30005         item.render(td);
30006         this.items.add(item);
30007         return item;
30008     },
30009     
30010     /**
30011      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
30012      * @param {Object/Array} config A button config or array of configs
30013      * @return {Roo.Toolbar.Button/Array}
30014      */
30015     addButton : function(config){
30016         if(config instanceof Array){
30017             var buttons = [];
30018             for(var i = 0, len = config.length; i < len; i++) {
30019                 buttons.push(this.addButton(config[i]));
30020             }
30021             return buttons;
30022         }
30023         var b = config;
30024         if(!(config instanceof Roo.Toolbar.Button)){
30025             b = config.split ?
30026                 new Roo.Toolbar.SplitButton(config) :
30027                 new Roo.Toolbar.Button(config);
30028         }
30029         var td = this.nextBlock();
30030         b.render(td);
30031         this.items.add(b);
30032         return b;
30033     },
30034     
30035     /**
30036      * Adds text to the toolbar
30037      * @param {String} text The text to add
30038      * @return {Roo.Toolbar.Item} The element's item
30039      */
30040     addText : function(text){
30041         return this.addItem(new Roo.Toolbar.TextItem(text));
30042     },
30043     
30044     /**
30045      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
30046      * @param {Number} index The index where the item is to be inserted
30047      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
30048      * @return {Roo.Toolbar.Button/Item}
30049      */
30050     insertButton : function(index, item){
30051         if(item instanceof Array){
30052             var buttons = [];
30053             for(var i = 0, len = item.length; i < len; i++) {
30054                buttons.push(this.insertButton(index + i, item[i]));
30055             }
30056             return buttons;
30057         }
30058         if (!(item instanceof Roo.Toolbar.Button)){
30059            item = new Roo.Toolbar.Button(item);
30060         }
30061         var td = document.createElement("td");
30062         this.tr.insertBefore(td, this.tr.childNodes[index]);
30063         item.render(td);
30064         this.items.insert(index, item);
30065         return item;
30066     },
30067     
30068     /**
30069      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
30070      * @param {Object} config
30071      * @return {Roo.Toolbar.Item} The element's item
30072      */
30073     addDom : function(config, returnEl){
30074         var td = this.nextBlock();
30075         Roo.DomHelper.overwrite(td, config);
30076         var ti = new Roo.Toolbar.Item(td.firstChild);
30077         ti.render(td);
30078         this.items.add(ti);
30079         return ti;
30080     },
30081
30082     /**
30083      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30084      * @type Roo.util.MixedCollection  
30085      */
30086     fields : false,
30087     
30088     /**
30089      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30090      * Note: the field should not have been rendered yet. For a field that has already been
30091      * rendered, use {@link #addElement}.
30092      * @param {Roo.form.Field} field
30093      * @return {Roo.ToolbarItem}
30094      */
30095      
30096       
30097     addField : function(field) {
30098         if (!this.fields) {
30099             var autoId = 0;
30100             this.fields = new Roo.util.MixedCollection(false, function(o){
30101                 return o.id || ("item" + (++autoId));
30102             });
30103
30104         }
30105         
30106         var td = this.nextBlock();
30107         field.render(td);
30108         var ti = new Roo.Toolbar.Item(td.firstChild);
30109         ti.render(td);
30110         this.items.add(ti);
30111         this.fields.add(field);
30112         return ti;
30113     },
30114     /**
30115      * Hide the toolbar
30116      * @method hide
30117      */
30118      
30119       
30120     hide : function()
30121     {
30122         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30123         this.el.child('div').hide();
30124     },
30125     /**
30126      * Show the toolbar
30127      * @method show
30128      */
30129     show : function()
30130     {
30131         this.el.child('div').show();
30132     },
30133       
30134     // private
30135     nextBlock : function(){
30136         var td = document.createElement("td");
30137         this.tr.appendChild(td);
30138         return td;
30139     },
30140
30141     // private
30142     destroy : function(){
30143         if(this.items){ // rendered?
30144             Roo.destroy.apply(Roo, this.items.items);
30145         }
30146         if(this.fields){ // rendered?
30147             Roo.destroy.apply(Roo, this.fields.items);
30148         }
30149         Roo.Element.uncache(this.el, this.tr);
30150     }
30151 };
30152
30153 /**
30154  * @class Roo.Toolbar.Item
30155  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30156  * @constructor
30157  * Creates a new Item
30158  * @param {HTMLElement} el 
30159  */
30160 Roo.Toolbar.Item = function(el){
30161     var cfg = {};
30162     if (typeof (el.xtype) != 'undefined') {
30163         cfg = el;
30164         el = cfg.el;
30165     }
30166     
30167     this.el = Roo.getDom(el);
30168     this.id = Roo.id(this.el);
30169     this.hidden = false;
30170     
30171     this.addEvents({
30172          /**
30173              * @event render
30174              * Fires when the button is rendered
30175              * @param {Button} this
30176              */
30177         'render': true
30178     });
30179     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30180 };
30181 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30182 //Roo.Toolbar.Item.prototype = {
30183     
30184     /**
30185      * Get this item's HTML Element
30186      * @return {HTMLElement}
30187      */
30188     getEl : function(){
30189        return this.el;  
30190     },
30191
30192     // private
30193     render : function(td){
30194         
30195          this.td = td;
30196         td.appendChild(this.el);
30197         
30198         this.fireEvent('render', this);
30199     },
30200     
30201     /**
30202      * Removes and destroys this item.
30203      */
30204     destroy : function(){
30205         this.td.parentNode.removeChild(this.td);
30206     },
30207     
30208     /**
30209      * Shows this item.
30210      */
30211     show: function(){
30212         this.hidden = false;
30213         this.td.style.display = "";
30214     },
30215     
30216     /**
30217      * Hides this item.
30218      */
30219     hide: function(){
30220         this.hidden = true;
30221         this.td.style.display = "none";
30222     },
30223     
30224     /**
30225      * Convenience function for boolean show/hide.
30226      * @param {Boolean} visible true to show/false to hide
30227      */
30228     setVisible: function(visible){
30229         if(visible) {
30230             this.show();
30231         }else{
30232             this.hide();
30233         }
30234     },
30235     
30236     /**
30237      * Try to focus this item.
30238      */
30239     focus : function(){
30240         Roo.fly(this.el).focus();
30241     },
30242     
30243     /**
30244      * Disables this item.
30245      */
30246     disable : function(){
30247         Roo.fly(this.td).addClass("x-item-disabled");
30248         this.disabled = true;
30249         this.el.disabled = true;
30250     },
30251     
30252     /**
30253      * Enables this item.
30254      */
30255     enable : function(){
30256         Roo.fly(this.td).removeClass("x-item-disabled");
30257         this.disabled = false;
30258         this.el.disabled = false;
30259     }
30260 });
30261
30262
30263 /**
30264  * @class Roo.Toolbar.Separator
30265  * @extends Roo.Toolbar.Item
30266  * A simple toolbar separator class
30267  * @constructor
30268  * Creates a new Separator
30269  */
30270 Roo.Toolbar.Separator = function(cfg){
30271     
30272     var s = document.createElement("span");
30273     s.className = "ytb-sep";
30274     if (cfg) {
30275         cfg.el = s;
30276     }
30277     
30278     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30279 };
30280 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30281     enable:Roo.emptyFn,
30282     disable:Roo.emptyFn,
30283     focus:Roo.emptyFn
30284 });
30285
30286 /**
30287  * @class Roo.Toolbar.Spacer
30288  * @extends Roo.Toolbar.Item
30289  * A simple element that adds extra horizontal space to a toolbar.
30290  * @constructor
30291  * Creates a new Spacer
30292  */
30293 Roo.Toolbar.Spacer = function(cfg){
30294     var s = document.createElement("div");
30295     s.className = "ytb-spacer";
30296     if (cfg) {
30297         cfg.el = s;
30298     }
30299     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30300 };
30301 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30302     enable:Roo.emptyFn,
30303     disable:Roo.emptyFn,
30304     focus:Roo.emptyFn
30305 });
30306
30307 /**
30308  * @class Roo.Toolbar.Fill
30309  * @extends Roo.Toolbar.Spacer
30310  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30311  * @constructor
30312  * Creates a new Spacer
30313  */
30314 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30315     // private
30316     render : function(td){
30317         td.style.width = '100%';
30318         Roo.Toolbar.Fill.superclass.render.call(this, td);
30319     }
30320 });
30321
30322 /**
30323  * @class Roo.Toolbar.TextItem
30324  * @extends Roo.Toolbar.Item
30325  * A simple class that renders text directly into a toolbar.
30326  * @constructor
30327  * Creates a new TextItem
30328  * @param {String} text
30329  */
30330 Roo.Toolbar.TextItem = function(cfg){
30331     var  text = cfg || "";
30332     if (typeof(cfg) == 'object') {
30333         text = cfg.text || "";
30334     }  else {
30335         cfg = null;
30336     }
30337     var s = document.createElement("span");
30338     s.className = "ytb-text";
30339     s.innerHTML = text;
30340     if (cfg) {
30341         cfg.el  = s;
30342     }
30343     
30344     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30345 };
30346 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30347     
30348      
30349     enable:Roo.emptyFn,
30350     disable:Roo.emptyFn,
30351     focus:Roo.emptyFn
30352 });
30353
30354 /**
30355  * @class Roo.Toolbar.Button
30356  * @extends Roo.Button
30357  * A button that renders into a toolbar.
30358  * @constructor
30359  * Creates a new Button
30360  * @param {Object} config A standard {@link Roo.Button} config object
30361  */
30362 Roo.Toolbar.Button = function(config){
30363     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30364 };
30365 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30366     render : function(td){
30367         this.td = td;
30368         Roo.Toolbar.Button.superclass.render.call(this, td);
30369     },
30370     
30371     /**
30372      * Removes and destroys this button
30373      */
30374     destroy : function(){
30375         Roo.Toolbar.Button.superclass.destroy.call(this);
30376         this.td.parentNode.removeChild(this.td);
30377     },
30378     
30379     /**
30380      * Shows this button
30381      */
30382     show: function(){
30383         this.hidden = false;
30384         this.td.style.display = "";
30385     },
30386     
30387     /**
30388      * Hides this button
30389      */
30390     hide: function(){
30391         this.hidden = true;
30392         this.td.style.display = "none";
30393     },
30394
30395     /**
30396      * Disables this item
30397      */
30398     disable : function(){
30399         Roo.fly(this.td).addClass("x-item-disabled");
30400         this.disabled = true;
30401     },
30402
30403     /**
30404      * Enables this item
30405      */
30406     enable : function(){
30407         Roo.fly(this.td).removeClass("x-item-disabled");
30408         this.disabled = false;
30409     }
30410 });
30411 // backwards compat
30412 Roo.ToolbarButton = Roo.Toolbar.Button;
30413
30414 /**
30415  * @class Roo.Toolbar.SplitButton
30416  * @extends Roo.SplitButton
30417  * A menu button that renders into a toolbar.
30418  * @constructor
30419  * Creates a new SplitButton
30420  * @param {Object} config A standard {@link Roo.SplitButton} config object
30421  */
30422 Roo.Toolbar.SplitButton = function(config){
30423     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30424 };
30425 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30426     render : function(td){
30427         this.td = td;
30428         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30429     },
30430     
30431     /**
30432      * Removes and destroys this button
30433      */
30434     destroy : function(){
30435         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30436         this.td.parentNode.removeChild(this.td);
30437     },
30438     
30439     /**
30440      * Shows this button
30441      */
30442     show: function(){
30443         this.hidden = false;
30444         this.td.style.display = "";
30445     },
30446     
30447     /**
30448      * Hides this button
30449      */
30450     hide: function(){
30451         this.hidden = true;
30452         this.td.style.display = "none";
30453     }
30454 });
30455
30456 // backwards compat
30457 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30458  * Based on:
30459  * Ext JS Library 1.1.1
30460  * Copyright(c) 2006-2007, Ext JS, LLC.
30461  *
30462  * Originally Released Under LGPL - original licence link has changed is not relivant.
30463  *
30464  * Fork - LGPL
30465  * <script type="text/javascript">
30466  */
30467  
30468 /**
30469  * @class Roo.PagingToolbar
30470  * @extends Roo.Toolbar
30471  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30472  * @constructor
30473  * Create a new PagingToolbar
30474  * @param {Object} config The config object
30475  */
30476 Roo.PagingToolbar = function(el, ds, config)
30477 {
30478     // old args format still supported... - xtype is prefered..
30479     if (typeof(el) == 'object' && el.xtype) {
30480         // created from xtype...
30481         config = el;
30482         ds = el.dataSource;
30483         el = config.container;
30484     }
30485     var items = [];
30486     if (config.items) {
30487         items = config.items;
30488         config.items = [];
30489     }
30490     
30491     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30492     this.ds = ds;
30493     this.cursor = 0;
30494     this.renderButtons(this.el);
30495     this.bind(ds);
30496     
30497     // supprot items array.
30498    
30499     Roo.each(items, function(e) {
30500         this.add(Roo.factory(e));
30501     },this);
30502     
30503 };
30504
30505 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30506     /**
30507      * @cfg {Roo.data.Store} dataSource
30508      * The underlying data store providing the paged data
30509      */
30510     /**
30511      * @cfg {String/HTMLElement/Element} container
30512      * container The id or element that will contain the toolbar
30513      */
30514     /**
30515      * @cfg {Boolean} displayInfo
30516      * True to display the displayMsg (defaults to false)
30517      */
30518     /**
30519      * @cfg {Number} pageSize
30520      * The number of records to display per page (defaults to 20)
30521      */
30522     pageSize: 20,
30523     /**
30524      * @cfg {String} displayMsg
30525      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30526      */
30527     displayMsg : 'Displaying {0} - {1} of {2}',
30528     /**
30529      * @cfg {String} emptyMsg
30530      * The message to display when no records are found (defaults to "No data to display")
30531      */
30532     emptyMsg : 'No data to display',
30533     /**
30534      * Customizable piece of the default paging text (defaults to "Page")
30535      * @type String
30536      */
30537     beforePageText : "Page",
30538     /**
30539      * Customizable piece of the default paging text (defaults to "of %0")
30540      * @type String
30541      */
30542     afterPageText : "of {0}",
30543     /**
30544      * Customizable piece of the default paging text (defaults to "First Page")
30545      * @type String
30546      */
30547     firstText : "First Page",
30548     /**
30549      * Customizable piece of the default paging text (defaults to "Previous Page")
30550      * @type String
30551      */
30552     prevText : "Previous Page",
30553     /**
30554      * Customizable piece of the default paging text (defaults to "Next Page")
30555      * @type String
30556      */
30557     nextText : "Next Page",
30558     /**
30559      * Customizable piece of the default paging text (defaults to "Last Page")
30560      * @type String
30561      */
30562     lastText : "Last Page",
30563     /**
30564      * Customizable piece of the default paging text (defaults to "Refresh")
30565      * @type String
30566      */
30567     refreshText : "Refresh",
30568
30569     // private
30570     renderButtons : function(el){
30571         Roo.PagingToolbar.superclass.render.call(this, el);
30572         this.first = this.addButton({
30573             tooltip: this.firstText,
30574             cls: "x-btn-icon x-grid-page-first",
30575             disabled: true,
30576             handler: this.onClick.createDelegate(this, ["first"])
30577         });
30578         this.prev = this.addButton({
30579             tooltip: this.prevText,
30580             cls: "x-btn-icon x-grid-page-prev",
30581             disabled: true,
30582             handler: this.onClick.createDelegate(this, ["prev"])
30583         });
30584         //this.addSeparator();
30585         this.add(this.beforePageText);
30586         this.field = Roo.get(this.addDom({
30587            tag: "input",
30588            type: "text",
30589            size: "3",
30590            value: "1",
30591            cls: "x-grid-page-number"
30592         }).el);
30593         this.field.on("keydown", this.onPagingKeydown, this);
30594         this.field.on("focus", function(){this.dom.select();});
30595         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30596         this.field.setHeight(18);
30597         //this.addSeparator();
30598         this.next = this.addButton({
30599             tooltip: this.nextText,
30600             cls: "x-btn-icon x-grid-page-next",
30601             disabled: true,
30602             handler: this.onClick.createDelegate(this, ["next"])
30603         });
30604         this.last = this.addButton({
30605             tooltip: this.lastText,
30606             cls: "x-btn-icon x-grid-page-last",
30607             disabled: true,
30608             handler: this.onClick.createDelegate(this, ["last"])
30609         });
30610         //this.addSeparator();
30611         this.loading = this.addButton({
30612             tooltip: this.refreshText,
30613             cls: "x-btn-icon x-grid-loading",
30614             handler: this.onClick.createDelegate(this, ["refresh"])
30615         });
30616
30617         if(this.displayInfo){
30618             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30619         }
30620     },
30621
30622     // private
30623     updateInfo : function(){
30624         if(this.displayEl){
30625             var count = this.ds.getCount();
30626             var msg = count == 0 ?
30627                 this.emptyMsg :
30628                 String.format(
30629                     this.displayMsg,
30630                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30631                 );
30632             this.displayEl.update(msg);
30633         }
30634     },
30635
30636     // private
30637     onLoad : function(ds, r, o){
30638        this.cursor = o.params ? o.params.start : 0;
30639        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30640
30641        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30642        this.field.dom.value = ap;
30643        this.first.setDisabled(ap == 1);
30644        this.prev.setDisabled(ap == 1);
30645        this.next.setDisabled(ap == ps);
30646        this.last.setDisabled(ap == ps);
30647        this.loading.enable();
30648        this.updateInfo();
30649     },
30650
30651     // private
30652     getPageData : function(){
30653         var total = this.ds.getTotalCount();
30654         return {
30655             total : total,
30656             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30657             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30658         };
30659     },
30660
30661     // private
30662     onLoadError : function(){
30663         this.loading.enable();
30664     },
30665
30666     // private
30667     onPagingKeydown : function(e){
30668         var k = e.getKey();
30669         var d = this.getPageData();
30670         if(k == e.RETURN){
30671             var v = this.field.dom.value, pageNum;
30672             if(!v || isNaN(pageNum = parseInt(v, 10))){
30673                 this.field.dom.value = d.activePage;
30674                 return;
30675             }
30676             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30677             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30678             e.stopEvent();
30679         }
30680         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))
30681         {
30682           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30683           this.field.dom.value = pageNum;
30684           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30685           e.stopEvent();
30686         }
30687         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30688         {
30689           var v = this.field.dom.value, pageNum; 
30690           var increment = (e.shiftKey) ? 10 : 1;
30691           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30692             increment *= -1;
30693           }
30694           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30695             this.field.dom.value = d.activePage;
30696             return;
30697           }
30698           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30699           {
30700             this.field.dom.value = parseInt(v, 10) + increment;
30701             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30702             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30703           }
30704           e.stopEvent();
30705         }
30706     },
30707
30708     // private
30709     beforeLoad : function(){
30710         if(this.loading){
30711             this.loading.disable();
30712         }
30713     },
30714
30715     // private
30716     onClick : function(which){
30717         var ds = this.ds;
30718         switch(which){
30719             case "first":
30720                 ds.load({params:{start: 0, limit: this.pageSize}});
30721             break;
30722             case "prev":
30723                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30724             break;
30725             case "next":
30726                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30727             break;
30728             case "last":
30729                 var total = ds.getTotalCount();
30730                 var extra = total % this.pageSize;
30731                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30732                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30733             break;
30734             case "refresh":
30735                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30736             break;
30737         }
30738     },
30739
30740     /**
30741      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30742      * @param {Roo.data.Store} store The data store to unbind
30743      */
30744     unbind : function(ds){
30745         ds.un("beforeload", this.beforeLoad, this);
30746         ds.un("load", this.onLoad, this);
30747         ds.un("loadexception", this.onLoadError, this);
30748         ds.un("remove", this.updateInfo, this);
30749         ds.un("add", this.updateInfo, this);
30750         this.ds = undefined;
30751     },
30752
30753     /**
30754      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30755      * @param {Roo.data.Store} store The data store to bind
30756      */
30757     bind : function(ds){
30758         ds.on("beforeload", this.beforeLoad, this);
30759         ds.on("load", this.onLoad, this);
30760         ds.on("loadexception", this.onLoadError, this);
30761         ds.on("remove", this.updateInfo, this);
30762         ds.on("add", this.updateInfo, this);
30763         this.ds = ds;
30764     }
30765 });/*
30766  * Based on:
30767  * Ext JS Library 1.1.1
30768  * Copyright(c) 2006-2007, Ext JS, LLC.
30769  *
30770  * Originally Released Under LGPL - original licence link has changed is not relivant.
30771  *
30772  * Fork - LGPL
30773  * <script type="text/javascript">
30774  */
30775
30776 /**
30777  * @class Roo.Resizable
30778  * @extends Roo.util.Observable
30779  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30780  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30781  * 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
30782  * the element will be wrapped for you automatically.</p>
30783  * <p>Here is the list of valid resize handles:</p>
30784  * <pre>
30785 Value   Description
30786 ------  -------------------
30787  'n'     north
30788  's'     south
30789  'e'     east
30790  'w'     west
30791  'nw'    northwest
30792  'sw'    southwest
30793  'se'    southeast
30794  'ne'    northeast
30795  'hd'    horizontal drag
30796  'all'   all
30797 </pre>
30798  * <p>Here's an example showing the creation of a typical Resizable:</p>
30799  * <pre><code>
30800 var resizer = new Roo.Resizable("element-id", {
30801     handles: 'all',
30802     minWidth: 200,
30803     minHeight: 100,
30804     maxWidth: 500,
30805     maxHeight: 400,
30806     pinned: true
30807 });
30808 resizer.on("resize", myHandler);
30809 </code></pre>
30810  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30811  * resizer.east.setDisplayed(false);</p>
30812  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30813  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30814  * resize operation's new size (defaults to [0, 0])
30815  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30816  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30817  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30818  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30819  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30820  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30821  * @cfg {Number} width The width of the element in pixels (defaults to null)
30822  * @cfg {Number} height The height of the element in pixels (defaults to null)
30823  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30824  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30825  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30826  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
30827  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
30828  * in favor of the handles config option (defaults to false)
30829  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
30830  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
30831  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
30832  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
30833  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
30834  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
30835  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
30836  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
30837  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
30838  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
30839  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
30840  * @constructor
30841  * Create a new resizable component
30842  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
30843  * @param {Object} config configuration options
30844   */
30845 Roo.Resizable = function(el, config)
30846 {
30847     this.el = Roo.get(el);
30848
30849     if(config && config.wrap){
30850         config.resizeChild = this.el;
30851         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
30852         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
30853         this.el.setStyle("overflow", "hidden");
30854         this.el.setPositioning(config.resizeChild.getPositioning());
30855         config.resizeChild.clearPositioning();
30856         if(!config.width || !config.height){
30857             var csize = config.resizeChild.getSize();
30858             this.el.setSize(csize.width, csize.height);
30859         }
30860         if(config.pinned && !config.adjustments){
30861             config.adjustments = "auto";
30862         }
30863     }
30864
30865     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
30866     this.proxy.unselectable();
30867     this.proxy.enableDisplayMode('block');
30868
30869     Roo.apply(this, config);
30870
30871     if(this.pinned){
30872         this.disableTrackOver = true;
30873         this.el.addClass("x-resizable-pinned");
30874     }
30875     // if the element isn't positioned, make it relative
30876     var position = this.el.getStyle("position");
30877     if(position != "absolute" && position != "fixed"){
30878         this.el.setStyle("position", "relative");
30879     }
30880     if(!this.handles){ // no handles passed, must be legacy style
30881         this.handles = 's,e,se';
30882         if(this.multiDirectional){
30883             this.handles += ',n,w';
30884         }
30885     }
30886     if(this.handles == "all"){
30887         this.handles = "n s e w ne nw se sw";
30888     }
30889     var hs = this.handles.split(/\s*?[,;]\s*?| /);
30890     var ps = Roo.Resizable.positions;
30891     for(var i = 0, len = hs.length; i < len; i++){
30892         if(hs[i] && ps[hs[i]]){
30893             var pos = ps[hs[i]];
30894             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
30895         }
30896     }
30897     // legacy
30898     this.corner = this.southeast;
30899     
30900     // updateBox = the box can move..
30901     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
30902         this.updateBox = true;
30903     }
30904
30905     this.activeHandle = null;
30906
30907     if(this.resizeChild){
30908         if(typeof this.resizeChild == "boolean"){
30909             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
30910         }else{
30911             this.resizeChild = Roo.get(this.resizeChild, true);
30912         }
30913     }
30914     
30915     if(this.adjustments == "auto"){
30916         var rc = this.resizeChild;
30917         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
30918         if(rc && (hw || hn)){
30919             rc.position("relative");
30920             rc.setLeft(hw ? hw.el.getWidth() : 0);
30921             rc.setTop(hn ? hn.el.getHeight() : 0);
30922         }
30923         this.adjustments = [
30924             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
30925             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
30926         ];
30927     }
30928
30929     if(this.draggable){
30930         this.dd = this.dynamic ?
30931             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
30932         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
30933     }
30934
30935     // public events
30936     this.addEvents({
30937         /**
30938          * @event beforeresize
30939          * Fired before resize is allowed. Set enabled to false to cancel resize.
30940          * @param {Roo.Resizable} this
30941          * @param {Roo.EventObject} e The mousedown event
30942          */
30943         "beforeresize" : true,
30944         /**
30945          * @event resizing
30946          * Fired a resizing.
30947          * @param {Roo.Resizable} this
30948          * @param {Number} x The new x position
30949          * @param {Number} y The new y position
30950          * @param {Number} w The new w width
30951          * @param {Number} h The new h hight
30952          * @param {Roo.EventObject} e The mouseup event
30953          */
30954         "resizing" : true,
30955         /**
30956          * @event resize
30957          * Fired after a resize.
30958          * @param {Roo.Resizable} this
30959          * @param {Number} width The new width
30960          * @param {Number} height The new height
30961          * @param {Roo.EventObject} e The mouseup event
30962          */
30963         "resize" : true
30964     });
30965
30966     if(this.width !== null && this.height !== null){
30967         this.resizeTo(this.width, this.height);
30968     }else{
30969         this.updateChildSize();
30970     }
30971     if(Roo.isIE){
30972         this.el.dom.style.zoom = 1;
30973     }
30974     Roo.Resizable.superclass.constructor.call(this);
30975 };
30976
30977 Roo.extend(Roo.Resizable, Roo.util.Observable, {
30978         resizeChild : false,
30979         adjustments : [0, 0],
30980         minWidth : 5,
30981         minHeight : 5,
30982         maxWidth : 10000,
30983         maxHeight : 10000,
30984         enabled : true,
30985         animate : false,
30986         duration : .35,
30987         dynamic : false,
30988         handles : false,
30989         multiDirectional : false,
30990         disableTrackOver : false,
30991         easing : 'easeOutStrong',
30992         widthIncrement : 0,
30993         heightIncrement : 0,
30994         pinned : false,
30995         width : null,
30996         height : null,
30997         preserveRatio : false,
30998         transparent: false,
30999         minX: 0,
31000         minY: 0,
31001         draggable: false,
31002
31003         /**
31004          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
31005          */
31006         constrainTo: undefined,
31007         /**
31008          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
31009          */
31010         resizeRegion: undefined,
31011
31012
31013     /**
31014      * Perform a manual resize
31015      * @param {Number} width
31016      * @param {Number} height
31017      */
31018     resizeTo : function(width, height){
31019         this.el.setSize(width, height);
31020         this.updateChildSize();
31021         this.fireEvent("resize", this, width, height, null);
31022     },
31023
31024     // private
31025     startSizing : function(e, handle){
31026         this.fireEvent("beforeresize", this, e);
31027         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
31028
31029             if(!this.overlay){
31030                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
31031                 this.overlay.unselectable();
31032                 this.overlay.enableDisplayMode("block");
31033                 this.overlay.on("mousemove", this.onMouseMove, this);
31034                 this.overlay.on("mouseup", this.onMouseUp, this);
31035             }
31036             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
31037
31038             this.resizing = true;
31039             this.startBox = this.el.getBox();
31040             this.startPoint = e.getXY();
31041             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
31042                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
31043
31044             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31045             this.overlay.show();
31046
31047             if(this.constrainTo) {
31048                 var ct = Roo.get(this.constrainTo);
31049                 this.resizeRegion = ct.getRegion().adjust(
31050                     ct.getFrameWidth('t'),
31051                     ct.getFrameWidth('l'),
31052                     -ct.getFrameWidth('b'),
31053                     -ct.getFrameWidth('r')
31054                 );
31055             }
31056
31057             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
31058             this.proxy.show();
31059             this.proxy.setBox(this.startBox);
31060             if(!this.dynamic){
31061                 this.proxy.setStyle('visibility', 'visible');
31062             }
31063         }
31064     },
31065
31066     // private
31067     onMouseDown : function(handle, e){
31068         if(this.enabled){
31069             e.stopEvent();
31070             this.activeHandle = handle;
31071             this.startSizing(e, handle);
31072         }
31073     },
31074
31075     // private
31076     onMouseUp : function(e){
31077         var size = this.resizeElement();
31078         this.resizing = false;
31079         this.handleOut();
31080         this.overlay.hide();
31081         this.proxy.hide();
31082         this.fireEvent("resize", this, size.width, size.height, e);
31083     },
31084
31085     // private
31086     updateChildSize : function(){
31087         
31088         if(this.resizeChild){
31089             var el = this.el;
31090             var child = this.resizeChild;
31091             var adj = this.adjustments;
31092             if(el.dom.offsetWidth){
31093                 var b = el.getSize(true);
31094                 child.setSize(b.width+adj[0], b.height+adj[1]);
31095             }
31096             // Second call here for IE
31097             // The first call enables instant resizing and
31098             // the second call corrects scroll bars if they
31099             // exist
31100             if(Roo.isIE){
31101                 setTimeout(function(){
31102                     if(el.dom.offsetWidth){
31103                         var b = el.getSize(true);
31104                         child.setSize(b.width+adj[0], b.height+adj[1]);
31105                     }
31106                 }, 10);
31107             }
31108         }
31109     },
31110
31111     // private
31112     snap : function(value, inc, min){
31113         if(!inc || !value) {
31114             return value;
31115         }
31116         var newValue = value;
31117         var m = value % inc;
31118         if(m > 0){
31119             if(m > (inc/2)){
31120                 newValue = value + (inc-m);
31121             }else{
31122                 newValue = value - m;
31123             }
31124         }
31125         return Math.max(min, newValue);
31126     },
31127
31128     // private
31129     resizeElement : function(){
31130         var box = this.proxy.getBox();
31131         if(this.updateBox){
31132             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31133         }else{
31134             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31135         }
31136         this.updateChildSize();
31137         if(!this.dynamic){
31138             this.proxy.hide();
31139         }
31140         return box;
31141     },
31142
31143     // private
31144     constrain : function(v, diff, m, mx){
31145         if(v - diff < m){
31146             diff = v - m;
31147         }else if(v - diff > mx){
31148             diff = mx - v;
31149         }
31150         return diff;
31151     },
31152
31153     // private
31154     onMouseMove : function(e){
31155         
31156         if(this.enabled){
31157             try{// try catch so if something goes wrong the user doesn't get hung
31158
31159             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31160                 return;
31161             }
31162
31163             //var curXY = this.startPoint;
31164             var curSize = this.curSize || this.startBox;
31165             var x = this.startBox.x, y = this.startBox.y;
31166             var ox = x, oy = y;
31167             var w = curSize.width, h = curSize.height;
31168             var ow = w, oh = h;
31169             var mw = this.minWidth, mh = this.minHeight;
31170             var mxw = this.maxWidth, mxh = this.maxHeight;
31171             var wi = this.widthIncrement;
31172             var hi = this.heightIncrement;
31173
31174             var eventXY = e.getXY();
31175             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31176             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31177
31178             var pos = this.activeHandle.position;
31179
31180             switch(pos){
31181                 case "east":
31182                     w += diffX;
31183                     w = Math.min(Math.max(mw, w), mxw);
31184                     break;
31185              
31186                 case "south":
31187                     h += diffY;
31188                     h = Math.min(Math.max(mh, h), mxh);
31189                     break;
31190                 case "southeast":
31191                     w += diffX;
31192                     h += diffY;
31193                     w = Math.min(Math.max(mw, w), mxw);
31194                     h = Math.min(Math.max(mh, h), mxh);
31195                     break;
31196                 case "north":
31197                     diffY = this.constrain(h, diffY, mh, mxh);
31198                     y += diffY;
31199                     h -= diffY;
31200                     break;
31201                 case "hdrag":
31202                     
31203                     if (wi) {
31204                         var adiffX = Math.abs(diffX);
31205                         var sub = (adiffX % wi); // how much 
31206                         if (sub > (wi/2)) { // far enough to snap
31207                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31208                         } else {
31209                             // remove difference.. 
31210                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31211                         }
31212                     }
31213                     x += diffX;
31214                     x = Math.max(this.minX, x);
31215                     break;
31216                 case "west":
31217                     diffX = this.constrain(w, diffX, mw, mxw);
31218                     x += diffX;
31219                     w -= diffX;
31220                     break;
31221                 case "northeast":
31222                     w += diffX;
31223                     w = Math.min(Math.max(mw, w), mxw);
31224                     diffY = this.constrain(h, diffY, mh, mxh);
31225                     y += diffY;
31226                     h -= diffY;
31227                     break;
31228                 case "northwest":
31229                     diffX = this.constrain(w, diffX, mw, mxw);
31230                     diffY = this.constrain(h, diffY, mh, mxh);
31231                     y += diffY;
31232                     h -= diffY;
31233                     x += diffX;
31234                     w -= diffX;
31235                     break;
31236                case "southwest":
31237                     diffX = this.constrain(w, diffX, mw, mxw);
31238                     h += diffY;
31239                     h = Math.min(Math.max(mh, h), mxh);
31240                     x += diffX;
31241                     w -= diffX;
31242                     break;
31243             }
31244
31245             var sw = this.snap(w, wi, mw);
31246             var sh = this.snap(h, hi, mh);
31247             if(sw != w || sh != h){
31248                 switch(pos){
31249                     case "northeast":
31250                         y -= sh - h;
31251                     break;
31252                     case "north":
31253                         y -= sh - h;
31254                         break;
31255                     case "southwest":
31256                         x -= sw - w;
31257                     break;
31258                     case "west":
31259                         x -= sw - w;
31260                         break;
31261                     case "northwest":
31262                         x -= sw - w;
31263                         y -= sh - h;
31264                     break;
31265                 }
31266                 w = sw;
31267                 h = sh;
31268             }
31269
31270             if(this.preserveRatio){
31271                 switch(pos){
31272                     case "southeast":
31273                     case "east":
31274                         h = oh * (w/ow);
31275                         h = Math.min(Math.max(mh, h), mxh);
31276                         w = ow * (h/oh);
31277                        break;
31278                     case "south":
31279                         w = ow * (h/oh);
31280                         w = Math.min(Math.max(mw, w), mxw);
31281                         h = oh * (w/ow);
31282                         break;
31283                     case "northeast":
31284                         w = ow * (h/oh);
31285                         w = Math.min(Math.max(mw, w), mxw);
31286                         h = oh * (w/ow);
31287                     break;
31288                     case "north":
31289                         var tw = w;
31290                         w = ow * (h/oh);
31291                         w = Math.min(Math.max(mw, w), mxw);
31292                         h = oh * (w/ow);
31293                         x += (tw - w) / 2;
31294                         break;
31295                     case "southwest":
31296                         h = oh * (w/ow);
31297                         h = Math.min(Math.max(mh, h), mxh);
31298                         var tw = w;
31299                         w = ow * (h/oh);
31300                         x += tw - w;
31301                         break;
31302                     case "west":
31303                         var th = h;
31304                         h = oh * (w/ow);
31305                         h = Math.min(Math.max(mh, h), mxh);
31306                         y += (th - h) / 2;
31307                         var tw = w;
31308                         w = ow * (h/oh);
31309                         x += tw - w;
31310                        break;
31311                     case "northwest":
31312                         var tw = w;
31313                         var th = h;
31314                         h = oh * (w/ow);
31315                         h = Math.min(Math.max(mh, h), mxh);
31316                         w = ow * (h/oh);
31317                         y += th - h;
31318                         x += tw - w;
31319                        break;
31320
31321                 }
31322             }
31323             if (pos == 'hdrag') {
31324                 w = ow;
31325             }
31326             this.proxy.setBounds(x, y, w, h);
31327             if(this.dynamic){
31328                 this.resizeElement();
31329             }
31330             }catch(e){}
31331         }
31332         this.fireEvent("resizing", this, x, y, w, h, e);
31333     },
31334
31335     // private
31336     handleOver : function(){
31337         if(this.enabled){
31338             this.el.addClass("x-resizable-over");
31339         }
31340     },
31341
31342     // private
31343     handleOut : function(){
31344         if(!this.resizing){
31345             this.el.removeClass("x-resizable-over");
31346         }
31347     },
31348
31349     /**
31350      * Returns the element this component is bound to.
31351      * @return {Roo.Element}
31352      */
31353     getEl : function(){
31354         return this.el;
31355     },
31356
31357     /**
31358      * Returns the resizeChild element (or null).
31359      * @return {Roo.Element}
31360      */
31361     getResizeChild : function(){
31362         return this.resizeChild;
31363     },
31364     groupHandler : function()
31365     {
31366         
31367     },
31368     /**
31369      * Destroys this resizable. If the element was wrapped and
31370      * removeEl is not true then the element remains.
31371      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31372      */
31373     destroy : function(removeEl){
31374         this.proxy.remove();
31375         if(this.overlay){
31376             this.overlay.removeAllListeners();
31377             this.overlay.remove();
31378         }
31379         var ps = Roo.Resizable.positions;
31380         for(var k in ps){
31381             if(typeof ps[k] != "function" && this[ps[k]]){
31382                 var h = this[ps[k]];
31383                 h.el.removeAllListeners();
31384                 h.el.remove();
31385             }
31386         }
31387         if(removeEl){
31388             this.el.update("");
31389             this.el.remove();
31390         }
31391     }
31392 });
31393
31394 // private
31395 // hash to map config positions to true positions
31396 Roo.Resizable.positions = {
31397     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31398     hd: "hdrag"
31399 };
31400
31401 // private
31402 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31403     if(!this.tpl){
31404         // only initialize the template if resizable is used
31405         var tpl = Roo.DomHelper.createTemplate(
31406             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31407         );
31408         tpl.compile();
31409         Roo.Resizable.Handle.prototype.tpl = tpl;
31410     }
31411     this.position = pos;
31412     this.rz = rz;
31413     // show north drag fro topdra
31414     var handlepos = pos == 'hdrag' ? 'north' : pos;
31415     
31416     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31417     if (pos == 'hdrag') {
31418         this.el.setStyle('cursor', 'pointer');
31419     }
31420     this.el.unselectable();
31421     if(transparent){
31422         this.el.setOpacity(0);
31423     }
31424     this.el.on("mousedown", this.onMouseDown, this);
31425     if(!disableTrackOver){
31426         this.el.on("mouseover", this.onMouseOver, this);
31427         this.el.on("mouseout", this.onMouseOut, this);
31428     }
31429 };
31430
31431 // private
31432 Roo.Resizable.Handle.prototype = {
31433     afterResize : function(rz){
31434         Roo.log('after?');
31435         // do nothing
31436     },
31437     // private
31438     onMouseDown : function(e){
31439         this.rz.onMouseDown(this, e);
31440     },
31441     // private
31442     onMouseOver : function(e){
31443         this.rz.handleOver(this, e);
31444     },
31445     // private
31446     onMouseOut : function(e){
31447         this.rz.handleOut(this, e);
31448     }
31449 };/*
31450  * Based on:
31451  * Ext JS Library 1.1.1
31452  * Copyright(c) 2006-2007, Ext JS, LLC.
31453  *
31454  * Originally Released Under LGPL - original licence link has changed is not relivant.
31455  *
31456  * Fork - LGPL
31457  * <script type="text/javascript">
31458  */
31459
31460 /**
31461  * @class Roo.Editor
31462  * @extends Roo.Component
31463  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31464  * @constructor
31465  * Create a new Editor
31466  * @param {Roo.form.Field} field The Field object (or descendant)
31467  * @param {Object} config The config object
31468  */
31469 Roo.Editor = function(field, config){
31470     Roo.Editor.superclass.constructor.call(this, config);
31471     this.field = field;
31472     this.addEvents({
31473         /**
31474              * @event beforestartedit
31475              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31476              * false from the handler of this event.
31477              * @param {Editor} this
31478              * @param {Roo.Element} boundEl The underlying element bound to this editor
31479              * @param {Mixed} value The field value being set
31480              */
31481         "beforestartedit" : true,
31482         /**
31483              * @event startedit
31484              * Fires when this editor is displayed
31485              * @param {Roo.Element} boundEl The underlying element bound to this editor
31486              * @param {Mixed} value The starting field value
31487              */
31488         "startedit" : true,
31489         /**
31490              * @event beforecomplete
31491              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31492              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31493              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31494              * event will not fire since no edit actually occurred.
31495              * @param {Editor} this
31496              * @param {Mixed} value The current field value
31497              * @param {Mixed} startValue The original field value
31498              */
31499         "beforecomplete" : true,
31500         /**
31501              * @event complete
31502              * Fires after editing is complete and any changed value has been written to the underlying field.
31503              * @param {Editor} this
31504              * @param {Mixed} value The current field value
31505              * @param {Mixed} startValue The original field value
31506              */
31507         "complete" : true,
31508         /**
31509          * @event specialkey
31510          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31511          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31512          * @param {Roo.form.Field} this
31513          * @param {Roo.EventObject} e The event object
31514          */
31515         "specialkey" : true
31516     });
31517 };
31518
31519 Roo.extend(Roo.Editor, Roo.Component, {
31520     /**
31521      * @cfg {Boolean/String} autosize
31522      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31523      * or "height" to adopt the height only (defaults to false)
31524      */
31525     /**
31526      * @cfg {Boolean} revertInvalid
31527      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31528      * validation fails (defaults to true)
31529      */
31530     /**
31531      * @cfg {Boolean} ignoreNoChange
31532      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31533      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31534      * will never be ignored.
31535      */
31536     /**
31537      * @cfg {Boolean} hideEl
31538      * False to keep the bound element visible while the editor is displayed (defaults to true)
31539      */
31540     /**
31541      * @cfg {Mixed} value
31542      * The data value of the underlying field (defaults to "")
31543      */
31544     value : "",
31545     /**
31546      * @cfg {String} alignment
31547      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31548      */
31549     alignment: "c-c?",
31550     /**
31551      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31552      * for bottom-right shadow (defaults to "frame")
31553      */
31554     shadow : "frame",
31555     /**
31556      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31557      */
31558     constrain : false,
31559     /**
31560      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31561      */
31562     completeOnEnter : false,
31563     /**
31564      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31565      */
31566     cancelOnEsc : false,
31567     /**
31568      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31569      */
31570     updateEl : false,
31571
31572     // private
31573     onRender : function(ct, position){
31574         this.el = new Roo.Layer({
31575             shadow: this.shadow,
31576             cls: "x-editor",
31577             parentEl : ct,
31578             shim : this.shim,
31579             shadowOffset:4,
31580             id: this.id,
31581             constrain: this.constrain
31582         });
31583         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31584         if(this.field.msgTarget != 'title'){
31585             this.field.msgTarget = 'qtip';
31586         }
31587         this.field.render(this.el);
31588         if(Roo.isGecko){
31589             this.field.el.dom.setAttribute('autocomplete', 'off');
31590         }
31591         this.field.on("specialkey", this.onSpecialKey, this);
31592         if(this.swallowKeys){
31593             this.field.el.swallowEvent(['keydown','keypress']);
31594         }
31595         this.field.show();
31596         this.field.on("blur", this.onBlur, this);
31597         if(this.field.grow){
31598             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31599         }
31600     },
31601
31602     onSpecialKey : function(field, e)
31603     {
31604         //Roo.log('editor onSpecialKey');
31605         if(this.completeOnEnter && e.getKey() == e.ENTER){
31606             e.stopEvent();
31607             this.completeEdit();
31608             return;
31609         }
31610         // do not fire special key otherwise it might hide close the editor...
31611         if(e.getKey() == e.ENTER){    
31612             return;
31613         }
31614         if(this.cancelOnEsc && e.getKey() == e.ESC){
31615             this.cancelEdit();
31616             return;
31617         } 
31618         this.fireEvent('specialkey', field, e);
31619     
31620     },
31621
31622     /**
31623      * Starts the editing process and shows the editor.
31624      * @param {String/HTMLElement/Element} el The element to edit
31625      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31626       * to the innerHTML of el.
31627      */
31628     startEdit : function(el, value){
31629         if(this.editing){
31630             this.completeEdit();
31631         }
31632         this.boundEl = Roo.get(el);
31633         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31634         if(!this.rendered){
31635             this.render(this.parentEl || document.body);
31636         }
31637         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31638             return;
31639         }
31640         this.startValue = v;
31641         this.field.setValue(v);
31642         if(this.autoSize){
31643             var sz = this.boundEl.getSize();
31644             switch(this.autoSize){
31645                 case "width":
31646                 this.setSize(sz.width,  "");
31647                 break;
31648                 case "height":
31649                 this.setSize("",  sz.height);
31650                 break;
31651                 default:
31652                 this.setSize(sz.width,  sz.height);
31653             }
31654         }
31655         this.el.alignTo(this.boundEl, this.alignment);
31656         this.editing = true;
31657         if(Roo.QuickTips){
31658             Roo.QuickTips.disable();
31659         }
31660         this.show();
31661     },
31662
31663     /**
31664      * Sets the height and width of this editor.
31665      * @param {Number} width The new width
31666      * @param {Number} height The new height
31667      */
31668     setSize : function(w, h){
31669         this.field.setSize(w, h);
31670         if(this.el){
31671             this.el.sync();
31672         }
31673     },
31674
31675     /**
31676      * Realigns the editor to the bound field based on the current alignment config value.
31677      */
31678     realign : function(){
31679         this.el.alignTo(this.boundEl, this.alignment);
31680     },
31681
31682     /**
31683      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31684      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31685      */
31686     completeEdit : function(remainVisible){
31687         if(!this.editing){
31688             return;
31689         }
31690         var v = this.getValue();
31691         if(this.revertInvalid !== false && !this.field.isValid()){
31692             v = this.startValue;
31693             this.cancelEdit(true);
31694         }
31695         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31696             this.editing = false;
31697             this.hide();
31698             return;
31699         }
31700         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31701             this.editing = false;
31702             if(this.updateEl && this.boundEl){
31703                 this.boundEl.update(v);
31704             }
31705             if(remainVisible !== true){
31706                 this.hide();
31707             }
31708             this.fireEvent("complete", this, v, this.startValue);
31709         }
31710     },
31711
31712     // private
31713     onShow : function(){
31714         this.el.show();
31715         if(this.hideEl !== false){
31716             this.boundEl.hide();
31717         }
31718         this.field.show();
31719         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31720             this.fixIEFocus = true;
31721             this.deferredFocus.defer(50, this);
31722         }else{
31723             this.field.focus();
31724         }
31725         this.fireEvent("startedit", this.boundEl, this.startValue);
31726     },
31727
31728     deferredFocus : function(){
31729         if(this.editing){
31730             this.field.focus();
31731         }
31732     },
31733
31734     /**
31735      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31736      * reverted to the original starting value.
31737      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31738      * cancel (defaults to false)
31739      */
31740     cancelEdit : function(remainVisible){
31741         if(this.editing){
31742             this.setValue(this.startValue);
31743             if(remainVisible !== true){
31744                 this.hide();
31745             }
31746         }
31747     },
31748
31749     // private
31750     onBlur : function(){
31751         if(this.allowBlur !== true && this.editing){
31752             this.completeEdit();
31753         }
31754     },
31755
31756     // private
31757     onHide : function(){
31758         if(this.editing){
31759             this.completeEdit();
31760             return;
31761         }
31762         this.field.blur();
31763         if(this.field.collapse){
31764             this.field.collapse();
31765         }
31766         this.el.hide();
31767         if(this.hideEl !== false){
31768             this.boundEl.show();
31769         }
31770         if(Roo.QuickTips){
31771             Roo.QuickTips.enable();
31772         }
31773     },
31774
31775     /**
31776      * Sets the data value of the editor
31777      * @param {Mixed} value Any valid value supported by the underlying field
31778      */
31779     setValue : function(v){
31780         this.field.setValue(v);
31781     },
31782
31783     /**
31784      * Gets the data value of the editor
31785      * @return {Mixed} The data value
31786      */
31787     getValue : function(){
31788         return this.field.getValue();
31789     }
31790 });/*
31791  * Based on:
31792  * Ext JS Library 1.1.1
31793  * Copyright(c) 2006-2007, Ext JS, LLC.
31794  *
31795  * Originally Released Under LGPL - original licence link has changed is not relivant.
31796  *
31797  * Fork - LGPL
31798  * <script type="text/javascript">
31799  */
31800  
31801 /**
31802  * @class Roo.BasicDialog
31803  * @extends Roo.util.Observable
31804  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31805  * <pre><code>
31806 var dlg = new Roo.BasicDialog("my-dlg", {
31807     height: 200,
31808     width: 300,
31809     minHeight: 100,
31810     minWidth: 150,
31811     modal: true,
31812     proxyDrag: true,
31813     shadow: true
31814 });
31815 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31816 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31817 dlg.addButton('Cancel', dlg.hide, dlg);
31818 dlg.show();
31819 </code></pre>
31820   <b>A Dialog should always be a direct child of the body element.</b>
31821  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31822  * @cfg {String} title Default text to display in the title bar (defaults to null)
31823  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31824  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31825  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31826  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
31827  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
31828  * (defaults to null with no animation)
31829  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
31830  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
31831  * property for valid values (defaults to 'all')
31832  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
31833  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
31834  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
31835  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
31836  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
31837  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
31838  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
31839  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
31840  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
31841  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
31842  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
31843  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
31844  * draggable = true (defaults to false)
31845  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
31846  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31847  * shadow (defaults to false)
31848  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
31849  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
31850  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
31851  * @cfg {Array} buttons Array of buttons
31852  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
31853  * @constructor
31854  * Create a new BasicDialog.
31855  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
31856  * @param {Object} config Configuration options
31857  */
31858 Roo.BasicDialog = function(el, config){
31859     this.el = Roo.get(el);
31860     var dh = Roo.DomHelper;
31861     if(!this.el && config && config.autoCreate){
31862         if(typeof config.autoCreate == "object"){
31863             if(!config.autoCreate.id){
31864                 config.autoCreate.id = el;
31865             }
31866             this.el = dh.append(document.body,
31867                         config.autoCreate, true);
31868         }else{
31869             this.el = dh.append(document.body,
31870                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
31871         }
31872     }
31873     el = this.el;
31874     el.setDisplayed(true);
31875     el.hide = this.hideAction;
31876     this.id = el.id;
31877     el.addClass("x-dlg");
31878
31879     Roo.apply(this, config);
31880
31881     this.proxy = el.createProxy("x-dlg-proxy");
31882     this.proxy.hide = this.hideAction;
31883     this.proxy.setOpacity(.5);
31884     this.proxy.hide();
31885
31886     if(config.width){
31887         el.setWidth(config.width);
31888     }
31889     if(config.height){
31890         el.setHeight(config.height);
31891     }
31892     this.size = el.getSize();
31893     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
31894         this.xy = [config.x,config.y];
31895     }else{
31896         this.xy = el.getCenterXY(true);
31897     }
31898     /** The header element @type Roo.Element */
31899     this.header = el.child("> .x-dlg-hd");
31900     /** The body element @type Roo.Element */
31901     this.body = el.child("> .x-dlg-bd");
31902     /** The footer element @type Roo.Element */
31903     this.footer = el.child("> .x-dlg-ft");
31904
31905     if(!this.header){
31906         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
31907     }
31908     if(!this.body){
31909         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
31910     }
31911
31912     this.header.unselectable();
31913     if(this.title){
31914         this.header.update(this.title);
31915     }
31916     // this element allows the dialog to be focused for keyboard event
31917     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
31918     this.focusEl.swallowEvent("click", true);
31919
31920     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
31921
31922     // wrap the body and footer for special rendering
31923     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
31924     if(this.footer){
31925         this.bwrap.dom.appendChild(this.footer.dom);
31926     }
31927
31928     this.bg = this.el.createChild({
31929         tag: "div", cls:"x-dlg-bg",
31930         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
31931     });
31932     this.centerBg = this.bg.child("div.x-dlg-bg-center");
31933
31934
31935     if(this.autoScroll !== false && !this.autoTabs){
31936         this.body.setStyle("overflow", "auto");
31937     }
31938
31939     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
31940
31941     if(this.closable !== false){
31942         this.el.addClass("x-dlg-closable");
31943         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
31944         this.close.on("click", this.closeClick, this);
31945         this.close.addClassOnOver("x-dlg-close-over");
31946     }
31947     if(this.collapsible !== false){
31948         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
31949         this.collapseBtn.on("click", this.collapseClick, this);
31950         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
31951         this.header.on("dblclick", this.collapseClick, this);
31952     }
31953     if(this.resizable !== false){
31954         this.el.addClass("x-dlg-resizable");
31955         this.resizer = new Roo.Resizable(el, {
31956             minWidth: this.minWidth || 80,
31957             minHeight:this.minHeight || 80,
31958             handles: this.resizeHandles || "all",
31959             pinned: true
31960         });
31961         this.resizer.on("beforeresize", this.beforeResize, this);
31962         this.resizer.on("resize", this.onResize, this);
31963     }
31964     if(this.draggable !== false){
31965         el.addClass("x-dlg-draggable");
31966         if (!this.proxyDrag) {
31967             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
31968         }
31969         else {
31970             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
31971         }
31972         dd.setHandleElId(this.header.id);
31973         dd.endDrag = this.endMove.createDelegate(this);
31974         dd.startDrag = this.startMove.createDelegate(this);
31975         dd.onDrag = this.onDrag.createDelegate(this);
31976         dd.scroll = false;
31977         this.dd = dd;
31978     }
31979     if(this.modal){
31980         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
31981         this.mask.enableDisplayMode("block");
31982         this.mask.hide();
31983         this.el.addClass("x-dlg-modal");
31984     }
31985     if(this.shadow){
31986         this.shadow = new Roo.Shadow({
31987             mode : typeof this.shadow == "string" ? this.shadow : "sides",
31988             offset : this.shadowOffset
31989         });
31990     }else{
31991         this.shadowOffset = 0;
31992     }
31993     if(Roo.useShims && this.shim !== false){
31994         this.shim = this.el.createShim();
31995         this.shim.hide = this.hideAction;
31996         this.shim.hide();
31997     }else{
31998         this.shim = false;
31999     }
32000     if(this.autoTabs){
32001         this.initTabs();
32002     }
32003     if (this.buttons) { 
32004         var bts= this.buttons;
32005         this.buttons = [];
32006         Roo.each(bts, function(b) {
32007             this.addButton(b);
32008         }, this);
32009     }
32010     
32011     
32012     this.addEvents({
32013         /**
32014          * @event keydown
32015          * Fires when a key is pressed
32016          * @param {Roo.BasicDialog} this
32017          * @param {Roo.EventObject} e
32018          */
32019         "keydown" : true,
32020         /**
32021          * @event move
32022          * Fires when this dialog is moved by the user.
32023          * @param {Roo.BasicDialog} this
32024          * @param {Number} x The new page X
32025          * @param {Number} y The new page Y
32026          */
32027         "move" : true,
32028         /**
32029          * @event resize
32030          * Fires when this dialog is resized by the user.
32031          * @param {Roo.BasicDialog} this
32032          * @param {Number} width The new width
32033          * @param {Number} height The new height
32034          */
32035         "resize" : true,
32036         /**
32037          * @event beforehide
32038          * Fires before this dialog is hidden.
32039          * @param {Roo.BasicDialog} this
32040          */
32041         "beforehide" : true,
32042         /**
32043          * @event hide
32044          * Fires when this dialog is hidden.
32045          * @param {Roo.BasicDialog} this
32046          */
32047         "hide" : true,
32048         /**
32049          * @event beforeshow
32050          * Fires before this dialog is shown.
32051          * @param {Roo.BasicDialog} this
32052          */
32053         "beforeshow" : true,
32054         /**
32055          * @event show
32056          * Fires when this dialog is shown.
32057          * @param {Roo.BasicDialog} this
32058          */
32059         "show" : true
32060     });
32061     el.on("keydown", this.onKeyDown, this);
32062     el.on("mousedown", this.toFront, this);
32063     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
32064     this.el.hide();
32065     Roo.DialogManager.register(this);
32066     Roo.BasicDialog.superclass.constructor.call(this);
32067 };
32068
32069 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
32070     shadowOffset: Roo.isIE ? 6 : 5,
32071     minHeight: 80,
32072     minWidth: 200,
32073     minButtonWidth: 75,
32074     defaultButton: null,
32075     buttonAlign: "right",
32076     tabTag: 'div',
32077     firstShow: true,
32078
32079     /**
32080      * Sets the dialog title text
32081      * @param {String} text The title text to display
32082      * @return {Roo.BasicDialog} this
32083      */
32084     setTitle : function(text){
32085         this.header.update(text);
32086         return this;
32087     },
32088
32089     // private
32090     closeClick : function(){
32091         this.hide();
32092     },
32093
32094     // private
32095     collapseClick : function(){
32096         this[this.collapsed ? "expand" : "collapse"]();
32097     },
32098
32099     /**
32100      * Collapses the dialog to its minimized state (only the title bar is visible).
32101      * Equivalent to the user clicking the collapse dialog button.
32102      */
32103     collapse : function(){
32104         if(!this.collapsed){
32105             this.collapsed = true;
32106             this.el.addClass("x-dlg-collapsed");
32107             this.restoreHeight = this.el.getHeight();
32108             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32109         }
32110     },
32111
32112     /**
32113      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32114      * clicking the expand dialog button.
32115      */
32116     expand : function(){
32117         if(this.collapsed){
32118             this.collapsed = false;
32119             this.el.removeClass("x-dlg-collapsed");
32120             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32121         }
32122     },
32123
32124     /**
32125      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32126      * @return {Roo.TabPanel} The tabs component
32127      */
32128     initTabs : function(){
32129         var tabs = this.getTabs();
32130         while(tabs.getTab(0)){
32131             tabs.removeTab(0);
32132         }
32133         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32134             var dom = el.dom;
32135             tabs.addTab(Roo.id(dom), dom.title);
32136             dom.title = "";
32137         });
32138         tabs.activate(0);
32139         return tabs;
32140     },
32141
32142     // private
32143     beforeResize : function(){
32144         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32145     },
32146
32147     // private
32148     onResize : function(){
32149         this.refreshSize();
32150         this.syncBodyHeight();
32151         this.adjustAssets();
32152         this.focus();
32153         this.fireEvent("resize", this, this.size.width, this.size.height);
32154     },
32155
32156     // private
32157     onKeyDown : function(e){
32158         if(this.isVisible()){
32159             this.fireEvent("keydown", this, e);
32160         }
32161     },
32162
32163     /**
32164      * Resizes the dialog.
32165      * @param {Number} width
32166      * @param {Number} height
32167      * @return {Roo.BasicDialog} this
32168      */
32169     resizeTo : function(width, height){
32170         this.el.setSize(width, height);
32171         this.size = {width: width, height: height};
32172         this.syncBodyHeight();
32173         if(this.fixedcenter){
32174             this.center();
32175         }
32176         if(this.isVisible()){
32177             this.constrainXY();
32178             this.adjustAssets();
32179         }
32180         this.fireEvent("resize", this, width, height);
32181         return this;
32182     },
32183
32184
32185     /**
32186      * Resizes the dialog to fit the specified content size.
32187      * @param {Number} width
32188      * @param {Number} height
32189      * @return {Roo.BasicDialog} this
32190      */
32191     setContentSize : function(w, h){
32192         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32193         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32194         //if(!this.el.isBorderBox()){
32195             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32196             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32197         //}
32198         if(this.tabs){
32199             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32200             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32201         }
32202         this.resizeTo(w, h);
32203         return this;
32204     },
32205
32206     /**
32207      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32208      * executed in response to a particular key being pressed while the dialog is active.
32209      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32210      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32211      * @param {Function} fn The function to call
32212      * @param {Object} scope (optional) The scope of the function
32213      * @return {Roo.BasicDialog} this
32214      */
32215     addKeyListener : function(key, fn, scope){
32216         var keyCode, shift, ctrl, alt;
32217         if(typeof key == "object" && !(key instanceof Array)){
32218             keyCode = key["key"];
32219             shift = key["shift"];
32220             ctrl = key["ctrl"];
32221             alt = key["alt"];
32222         }else{
32223             keyCode = key;
32224         }
32225         var handler = function(dlg, e){
32226             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32227                 var k = e.getKey();
32228                 if(keyCode instanceof Array){
32229                     for(var i = 0, len = keyCode.length; i < len; i++){
32230                         if(keyCode[i] == k){
32231                           fn.call(scope || window, dlg, k, e);
32232                           return;
32233                         }
32234                     }
32235                 }else{
32236                     if(k == keyCode){
32237                         fn.call(scope || window, dlg, k, e);
32238                     }
32239                 }
32240             }
32241         };
32242         this.on("keydown", handler);
32243         return this;
32244     },
32245
32246     /**
32247      * Returns the TabPanel component (creates it if it doesn't exist).
32248      * Note: If you wish to simply check for the existence of tabs without creating them,
32249      * check for a null 'tabs' property.
32250      * @return {Roo.TabPanel} The tabs component
32251      */
32252     getTabs : function(){
32253         if(!this.tabs){
32254             this.el.addClass("x-dlg-auto-tabs");
32255             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32256             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32257         }
32258         return this.tabs;
32259     },
32260
32261     /**
32262      * Adds a button to the footer section of the dialog.
32263      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32264      * object or a valid Roo.DomHelper element config
32265      * @param {Function} handler The function called when the button is clicked
32266      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32267      * @return {Roo.Button} The new button
32268      */
32269     addButton : function(config, handler, scope){
32270         var dh = Roo.DomHelper;
32271         if(!this.footer){
32272             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32273         }
32274         if(!this.btnContainer){
32275             var tb = this.footer.createChild({
32276
32277                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32278                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32279             }, null, true);
32280             this.btnContainer = tb.firstChild.firstChild.firstChild;
32281         }
32282         var bconfig = {
32283             handler: handler,
32284             scope: scope,
32285             minWidth: this.minButtonWidth,
32286             hideParent:true
32287         };
32288         if(typeof config == "string"){
32289             bconfig.text = config;
32290         }else{
32291             if(config.tag){
32292                 bconfig.dhconfig = config;
32293             }else{
32294                 Roo.apply(bconfig, config);
32295             }
32296         }
32297         var fc = false;
32298         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32299             bconfig.position = Math.max(0, bconfig.position);
32300             fc = this.btnContainer.childNodes[bconfig.position];
32301         }
32302          
32303         var btn = new Roo.Button(
32304             fc ? 
32305                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32306                 : this.btnContainer.appendChild(document.createElement("td")),
32307             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32308             bconfig
32309         );
32310         this.syncBodyHeight();
32311         if(!this.buttons){
32312             /**
32313              * Array of all the buttons that have been added to this dialog via addButton
32314              * @type Array
32315              */
32316             this.buttons = [];
32317         }
32318         this.buttons.push(btn);
32319         return btn;
32320     },
32321
32322     /**
32323      * Sets the default button to be focused when the dialog is displayed.
32324      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32325      * @return {Roo.BasicDialog} this
32326      */
32327     setDefaultButton : function(btn){
32328         this.defaultButton = btn;
32329         return this;
32330     },
32331
32332     // private
32333     getHeaderFooterHeight : function(safe){
32334         var height = 0;
32335         if(this.header){
32336            height += this.header.getHeight();
32337         }
32338         if(this.footer){
32339            var fm = this.footer.getMargins();
32340             height += (this.footer.getHeight()+fm.top+fm.bottom);
32341         }
32342         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32343         height += this.centerBg.getPadding("tb");
32344         return height;
32345     },
32346
32347     // private
32348     syncBodyHeight : function()
32349     {
32350         var bd = this.body, // the text
32351             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32352             bw = this.bwrap;
32353         var height = this.size.height - this.getHeaderFooterHeight(false);
32354         bd.setHeight(height-bd.getMargins("tb"));
32355         var hh = this.header.getHeight();
32356         var h = this.size.height-hh;
32357         cb.setHeight(h);
32358         
32359         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32360         bw.setHeight(h-cb.getPadding("tb"));
32361         
32362         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32363         bd.setWidth(bw.getWidth(true));
32364         if(this.tabs){
32365             this.tabs.syncHeight();
32366             if(Roo.isIE){
32367                 this.tabs.el.repaint();
32368             }
32369         }
32370     },
32371
32372     /**
32373      * Restores the previous state of the dialog if Roo.state is configured.
32374      * @return {Roo.BasicDialog} this
32375      */
32376     restoreState : function(){
32377         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32378         if(box && box.width){
32379             this.xy = [box.x, box.y];
32380             this.resizeTo(box.width, box.height);
32381         }
32382         return this;
32383     },
32384
32385     // private
32386     beforeShow : function(){
32387         this.expand();
32388         if(this.fixedcenter){
32389             this.xy = this.el.getCenterXY(true);
32390         }
32391         if(this.modal){
32392             Roo.get(document.body).addClass("x-body-masked");
32393             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32394             this.mask.show();
32395         }
32396         this.constrainXY();
32397     },
32398
32399     // private
32400     animShow : function(){
32401         var b = Roo.get(this.animateTarget).getBox();
32402         this.proxy.setSize(b.width, b.height);
32403         this.proxy.setLocation(b.x, b.y);
32404         this.proxy.show();
32405         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32406                     true, .35, this.showEl.createDelegate(this));
32407     },
32408
32409     /**
32410      * Shows the dialog.
32411      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32412      * @return {Roo.BasicDialog} this
32413      */
32414     show : function(animateTarget){
32415         if (this.fireEvent("beforeshow", this) === false){
32416             return;
32417         }
32418         if(this.syncHeightBeforeShow){
32419             this.syncBodyHeight();
32420         }else if(this.firstShow){
32421             this.firstShow = false;
32422             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32423         }
32424         this.animateTarget = animateTarget || this.animateTarget;
32425         if(!this.el.isVisible()){
32426             this.beforeShow();
32427             if(this.animateTarget && Roo.get(this.animateTarget)){
32428                 this.animShow();
32429             }else{
32430                 this.showEl();
32431             }
32432         }
32433         return this;
32434     },
32435
32436     // private
32437     showEl : function(){
32438         this.proxy.hide();
32439         this.el.setXY(this.xy);
32440         this.el.show();
32441         this.adjustAssets(true);
32442         this.toFront();
32443         this.focus();
32444         // IE peekaboo bug - fix found by Dave Fenwick
32445         if(Roo.isIE){
32446             this.el.repaint();
32447         }
32448         this.fireEvent("show", this);
32449     },
32450
32451     /**
32452      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32453      * dialog itself will receive focus.
32454      */
32455     focus : function(){
32456         if(this.defaultButton){
32457             this.defaultButton.focus();
32458         }else{
32459             this.focusEl.focus();
32460         }
32461     },
32462
32463     // private
32464     constrainXY : function(){
32465         if(this.constraintoviewport !== false){
32466             if(!this.viewSize){
32467                 if(this.container){
32468                     var s = this.container.getSize();
32469                     this.viewSize = [s.width, s.height];
32470                 }else{
32471                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32472                 }
32473             }
32474             var s = Roo.get(this.container||document).getScroll();
32475
32476             var x = this.xy[0], y = this.xy[1];
32477             var w = this.size.width, h = this.size.height;
32478             var vw = this.viewSize[0], vh = this.viewSize[1];
32479             // only move it if it needs it
32480             var moved = false;
32481             // first validate right/bottom
32482             if(x + w > vw+s.left){
32483                 x = vw - w;
32484                 moved = true;
32485             }
32486             if(y + h > vh+s.top){
32487                 y = vh - h;
32488                 moved = true;
32489             }
32490             // then make sure top/left isn't negative
32491             if(x < s.left){
32492                 x = s.left;
32493                 moved = true;
32494             }
32495             if(y < s.top){
32496                 y = s.top;
32497                 moved = true;
32498             }
32499             if(moved){
32500                 // cache xy
32501                 this.xy = [x, y];
32502                 if(this.isVisible()){
32503                     this.el.setLocation(x, y);
32504                     this.adjustAssets();
32505                 }
32506             }
32507         }
32508     },
32509
32510     // private
32511     onDrag : function(){
32512         if(!this.proxyDrag){
32513             this.xy = this.el.getXY();
32514             this.adjustAssets();
32515         }
32516     },
32517
32518     // private
32519     adjustAssets : function(doShow){
32520         var x = this.xy[0], y = this.xy[1];
32521         var w = this.size.width, h = this.size.height;
32522         if(doShow === true){
32523             if(this.shadow){
32524                 this.shadow.show(this.el);
32525             }
32526             if(this.shim){
32527                 this.shim.show();
32528             }
32529         }
32530         if(this.shadow && this.shadow.isVisible()){
32531             this.shadow.show(this.el);
32532         }
32533         if(this.shim && this.shim.isVisible()){
32534             this.shim.setBounds(x, y, w, h);
32535         }
32536     },
32537
32538     // private
32539     adjustViewport : function(w, h){
32540         if(!w || !h){
32541             w = Roo.lib.Dom.getViewWidth();
32542             h = Roo.lib.Dom.getViewHeight();
32543         }
32544         // cache the size
32545         this.viewSize = [w, h];
32546         if(this.modal && this.mask.isVisible()){
32547             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32548             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32549         }
32550         if(this.isVisible()){
32551             this.constrainXY();
32552         }
32553     },
32554
32555     /**
32556      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32557      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32558      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32559      */
32560     destroy : function(removeEl){
32561         if(this.isVisible()){
32562             this.animateTarget = null;
32563             this.hide();
32564         }
32565         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32566         if(this.tabs){
32567             this.tabs.destroy(removeEl);
32568         }
32569         Roo.destroy(
32570              this.shim,
32571              this.proxy,
32572              this.resizer,
32573              this.close,
32574              this.mask
32575         );
32576         if(this.dd){
32577             this.dd.unreg();
32578         }
32579         if(this.buttons){
32580            for(var i = 0, len = this.buttons.length; i < len; i++){
32581                this.buttons[i].destroy();
32582            }
32583         }
32584         this.el.removeAllListeners();
32585         if(removeEl === true){
32586             this.el.update("");
32587             this.el.remove();
32588         }
32589         Roo.DialogManager.unregister(this);
32590     },
32591
32592     // private
32593     startMove : function(){
32594         if(this.proxyDrag){
32595             this.proxy.show();
32596         }
32597         if(this.constraintoviewport !== false){
32598             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32599         }
32600     },
32601
32602     // private
32603     endMove : function(){
32604         if(!this.proxyDrag){
32605             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32606         }else{
32607             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32608             this.proxy.hide();
32609         }
32610         this.refreshSize();
32611         this.adjustAssets();
32612         this.focus();
32613         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32614     },
32615
32616     /**
32617      * Brings this dialog to the front of any other visible dialogs
32618      * @return {Roo.BasicDialog} this
32619      */
32620     toFront : function(){
32621         Roo.DialogManager.bringToFront(this);
32622         return this;
32623     },
32624
32625     /**
32626      * Sends this dialog to the back (under) of any other visible dialogs
32627      * @return {Roo.BasicDialog} this
32628      */
32629     toBack : function(){
32630         Roo.DialogManager.sendToBack(this);
32631         return this;
32632     },
32633
32634     /**
32635      * Centers this dialog in the viewport
32636      * @return {Roo.BasicDialog} this
32637      */
32638     center : function(){
32639         var xy = this.el.getCenterXY(true);
32640         this.moveTo(xy[0], xy[1]);
32641         return this;
32642     },
32643
32644     /**
32645      * Moves the dialog's top-left corner to the specified point
32646      * @param {Number} x
32647      * @param {Number} y
32648      * @return {Roo.BasicDialog} this
32649      */
32650     moveTo : function(x, y){
32651         this.xy = [x,y];
32652         if(this.isVisible()){
32653             this.el.setXY(this.xy);
32654             this.adjustAssets();
32655         }
32656         return this;
32657     },
32658
32659     /**
32660      * Aligns the dialog to the specified element
32661      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32662      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32663      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32664      * @return {Roo.BasicDialog} this
32665      */
32666     alignTo : function(element, position, offsets){
32667         this.xy = this.el.getAlignToXY(element, position, offsets);
32668         if(this.isVisible()){
32669             this.el.setXY(this.xy);
32670             this.adjustAssets();
32671         }
32672         return this;
32673     },
32674
32675     /**
32676      * Anchors an element to another element and realigns it when the window is resized.
32677      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32678      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32679      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32680      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32681      * is a number, it is used as the buffer delay (defaults to 50ms).
32682      * @return {Roo.BasicDialog} this
32683      */
32684     anchorTo : function(el, alignment, offsets, monitorScroll){
32685         var action = function(){
32686             this.alignTo(el, alignment, offsets);
32687         };
32688         Roo.EventManager.onWindowResize(action, this);
32689         var tm = typeof monitorScroll;
32690         if(tm != 'undefined'){
32691             Roo.EventManager.on(window, 'scroll', action, this,
32692                 {buffer: tm == 'number' ? monitorScroll : 50});
32693         }
32694         action.call(this);
32695         return this;
32696     },
32697
32698     /**
32699      * Returns true if the dialog is visible
32700      * @return {Boolean}
32701      */
32702     isVisible : function(){
32703         return this.el.isVisible();
32704     },
32705
32706     // private
32707     animHide : function(callback){
32708         var b = Roo.get(this.animateTarget).getBox();
32709         this.proxy.show();
32710         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32711         this.el.hide();
32712         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32713                     this.hideEl.createDelegate(this, [callback]));
32714     },
32715
32716     /**
32717      * Hides the dialog.
32718      * @param {Function} callback (optional) Function to call when the dialog is hidden
32719      * @return {Roo.BasicDialog} this
32720      */
32721     hide : function(callback){
32722         if (this.fireEvent("beforehide", this) === false){
32723             return;
32724         }
32725         if(this.shadow){
32726             this.shadow.hide();
32727         }
32728         if(this.shim) {
32729           this.shim.hide();
32730         }
32731         // sometimes animateTarget seems to get set.. causing problems...
32732         // this just double checks..
32733         if(this.animateTarget && Roo.get(this.animateTarget)) {
32734            this.animHide(callback);
32735         }else{
32736             this.el.hide();
32737             this.hideEl(callback);
32738         }
32739         return this;
32740     },
32741
32742     // private
32743     hideEl : function(callback){
32744         this.proxy.hide();
32745         if(this.modal){
32746             this.mask.hide();
32747             Roo.get(document.body).removeClass("x-body-masked");
32748         }
32749         this.fireEvent("hide", this);
32750         if(typeof callback == "function"){
32751             callback();
32752         }
32753     },
32754
32755     // private
32756     hideAction : function(){
32757         this.setLeft("-10000px");
32758         this.setTop("-10000px");
32759         this.setStyle("visibility", "hidden");
32760     },
32761
32762     // private
32763     refreshSize : function(){
32764         this.size = this.el.getSize();
32765         this.xy = this.el.getXY();
32766         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32767     },
32768
32769     // private
32770     // z-index is managed by the DialogManager and may be overwritten at any time
32771     setZIndex : function(index){
32772         if(this.modal){
32773             this.mask.setStyle("z-index", index);
32774         }
32775         if(this.shim){
32776             this.shim.setStyle("z-index", ++index);
32777         }
32778         if(this.shadow){
32779             this.shadow.setZIndex(++index);
32780         }
32781         this.el.setStyle("z-index", ++index);
32782         if(this.proxy){
32783             this.proxy.setStyle("z-index", ++index);
32784         }
32785         if(this.resizer){
32786             this.resizer.proxy.setStyle("z-index", ++index);
32787         }
32788
32789         this.lastZIndex = index;
32790     },
32791
32792     /**
32793      * Returns the element for this dialog
32794      * @return {Roo.Element} The underlying dialog Element
32795      */
32796     getEl : function(){
32797         return this.el;
32798     }
32799 });
32800
32801 /**
32802  * @class Roo.DialogManager
32803  * Provides global access to BasicDialogs that have been created and
32804  * support for z-indexing (layering) multiple open dialogs.
32805  */
32806 Roo.DialogManager = function(){
32807     var list = {};
32808     var accessList = [];
32809     var front = null;
32810
32811     // private
32812     var sortDialogs = function(d1, d2){
32813         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32814     };
32815
32816     // private
32817     var orderDialogs = function(){
32818         accessList.sort(sortDialogs);
32819         var seed = Roo.DialogManager.zseed;
32820         for(var i = 0, len = accessList.length; i < len; i++){
32821             var dlg = accessList[i];
32822             if(dlg){
32823                 dlg.setZIndex(seed + (i*10));
32824             }
32825         }
32826     };
32827
32828     return {
32829         /**
32830          * The starting z-index for BasicDialogs (defaults to 9000)
32831          * @type Number The z-index value
32832          */
32833         zseed : 9000,
32834
32835         // private
32836         register : function(dlg){
32837             list[dlg.id] = dlg;
32838             accessList.push(dlg);
32839         },
32840
32841         // private
32842         unregister : function(dlg){
32843             delete list[dlg.id];
32844             var i=0;
32845             var len=0;
32846             if(!accessList.indexOf){
32847                 for(  i = 0, len = accessList.length; i < len; i++){
32848                     if(accessList[i] == dlg){
32849                         accessList.splice(i, 1);
32850                         return;
32851                     }
32852                 }
32853             }else{
32854                  i = accessList.indexOf(dlg);
32855                 if(i != -1){
32856                     accessList.splice(i, 1);
32857                 }
32858             }
32859         },
32860
32861         /**
32862          * Gets a registered dialog by id
32863          * @param {String/Object} id The id of the dialog or a dialog
32864          * @return {Roo.BasicDialog} this
32865          */
32866         get : function(id){
32867             return typeof id == "object" ? id : list[id];
32868         },
32869
32870         /**
32871          * Brings the specified dialog to the front
32872          * @param {String/Object} dlg The id of the dialog or a dialog
32873          * @return {Roo.BasicDialog} this
32874          */
32875         bringToFront : function(dlg){
32876             dlg = this.get(dlg);
32877             if(dlg != front){
32878                 front = dlg;
32879                 dlg._lastAccess = new Date().getTime();
32880                 orderDialogs();
32881             }
32882             return dlg;
32883         },
32884
32885         /**
32886          * Sends the specified dialog to the back
32887          * @param {String/Object} dlg The id of the dialog or a dialog
32888          * @return {Roo.BasicDialog} this
32889          */
32890         sendToBack : function(dlg){
32891             dlg = this.get(dlg);
32892             dlg._lastAccess = -(new Date().getTime());
32893             orderDialogs();
32894             return dlg;
32895         },
32896
32897         /**
32898          * Hides all dialogs
32899          */
32900         hideAll : function(){
32901             for(var id in list){
32902                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
32903                     list[id].hide();
32904                 }
32905             }
32906         }
32907     };
32908 }();
32909
32910 /**
32911  * @class Roo.LayoutDialog
32912  * @extends Roo.BasicDialog
32913  * Dialog which provides adjustments for working with a layout in a Dialog.
32914  * Add your necessary layout config options to the dialog's config.<br>
32915  * Example usage (including a nested layout):
32916  * <pre><code>
32917 if(!dialog){
32918     dialog = new Roo.LayoutDialog("download-dlg", {
32919         modal: true,
32920         width:600,
32921         height:450,
32922         shadow:true,
32923         minWidth:500,
32924         minHeight:350,
32925         autoTabs:true,
32926         proxyDrag:true,
32927         // layout config merges with the dialog config
32928         center:{
32929             tabPosition: "top",
32930             alwaysShowTabs: true
32931         }
32932     });
32933     dialog.addKeyListener(27, dialog.hide, dialog);
32934     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
32935     dialog.addButton("Build It!", this.getDownload, this);
32936
32937     // we can even add nested layouts
32938     var innerLayout = new Roo.BorderLayout("dl-inner", {
32939         east: {
32940             initialSize: 200,
32941             autoScroll:true,
32942             split:true
32943         },
32944         center: {
32945             autoScroll:true
32946         }
32947     });
32948     innerLayout.beginUpdate();
32949     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
32950     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
32951     innerLayout.endUpdate(true);
32952
32953     var layout = dialog.getLayout();
32954     layout.beginUpdate();
32955     layout.add("center", new Roo.ContentPanel("standard-panel",
32956                         {title: "Download the Source", fitToFrame:true}));
32957     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
32958                {title: "Build your own roo.js"}));
32959     layout.getRegion("center").showPanel(sp);
32960     layout.endUpdate();
32961 }
32962 </code></pre>
32963     * @constructor
32964     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
32965     * @param {Object} config configuration options
32966   */
32967 Roo.LayoutDialog = function(el, cfg){
32968     
32969     var config=  cfg;
32970     if (typeof(cfg) == 'undefined') {
32971         config = Roo.apply({}, el);
32972         // not sure why we use documentElement here.. - it should always be body.
32973         // IE7 borks horribly if we use documentElement.
32974         // webkit also does not like documentElement - it creates a body element...
32975         el = Roo.get( document.body || document.documentElement ).createChild();
32976         //config.autoCreate = true;
32977     }
32978     
32979     
32980     config.autoTabs = false;
32981     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
32982     this.body.setStyle({overflow:"hidden", position:"relative"});
32983     this.layout = new Roo.BorderLayout(this.body.dom, config);
32984     this.layout.monitorWindowResize = false;
32985     this.el.addClass("x-dlg-auto-layout");
32986     // fix case when center region overwrites center function
32987     this.center = Roo.BasicDialog.prototype.center;
32988     this.on("show", this.layout.layout, this.layout, true);
32989     if (config.items) {
32990         var xitems = config.items;
32991         delete config.items;
32992         Roo.each(xitems, this.addxtype, this);
32993     }
32994     
32995     
32996 };
32997 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
32998     /**
32999      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
33000      * @deprecated
33001      */
33002     endUpdate : function(){
33003         this.layout.endUpdate();
33004     },
33005
33006     /**
33007      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
33008      *  @deprecated
33009      */
33010     beginUpdate : function(){
33011         this.layout.beginUpdate();
33012     },
33013
33014     /**
33015      * Get the BorderLayout for this dialog
33016      * @return {Roo.BorderLayout}
33017      */
33018     getLayout : function(){
33019         return this.layout;
33020     },
33021
33022     showEl : function(){
33023         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
33024         if(Roo.isIE7){
33025             this.layout.layout();
33026         }
33027     },
33028
33029     // private
33030     // Use the syncHeightBeforeShow config option to control this automatically
33031     syncBodyHeight : function(){
33032         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
33033         if(this.layout){this.layout.layout();}
33034     },
33035     
33036       /**
33037      * Add an xtype element (actually adds to the layout.)
33038      * @return {Object} xdata xtype object data.
33039      */
33040     
33041     addxtype : function(c) {
33042         return this.layout.addxtype(c);
33043     }
33044 });/*
33045  * Based on:
33046  * Ext JS Library 1.1.1
33047  * Copyright(c) 2006-2007, Ext JS, LLC.
33048  *
33049  * Originally Released Under LGPL - original licence link has changed is not relivant.
33050  *
33051  * Fork - LGPL
33052  * <script type="text/javascript">
33053  */
33054  
33055 /**
33056  * @class Roo.MessageBox
33057  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
33058  * Example usage:
33059  *<pre><code>
33060 // Basic alert:
33061 Roo.Msg.alert('Status', 'Changes saved successfully.');
33062
33063 // Prompt for user data:
33064 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
33065     if (btn == 'ok'){
33066         // process text value...
33067     }
33068 });
33069
33070 // Show a dialog using config options:
33071 Roo.Msg.show({
33072    title:'Save Changes?',
33073    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
33074    buttons: Roo.Msg.YESNOCANCEL,
33075    fn: processResult,
33076    animEl: 'elId'
33077 });
33078 </code></pre>
33079  * @singleton
33080  */
33081 Roo.MessageBox = function(){
33082     var dlg, opt, mask, waitTimer;
33083     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33084     var buttons, activeTextEl, bwidth;
33085
33086     // private
33087     var handleButton = function(button){
33088         dlg.hide();
33089         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33090     };
33091
33092     // private
33093     var handleHide = function(){
33094         if(opt && opt.cls){
33095             dlg.el.removeClass(opt.cls);
33096         }
33097         if(waitTimer){
33098             Roo.TaskMgr.stop(waitTimer);
33099             waitTimer = null;
33100         }
33101     };
33102
33103     // private
33104     var updateButtons = function(b){
33105         var width = 0;
33106         if(!b){
33107             buttons["ok"].hide();
33108             buttons["cancel"].hide();
33109             buttons["yes"].hide();
33110             buttons["no"].hide();
33111             dlg.footer.dom.style.display = 'none';
33112             return width;
33113         }
33114         dlg.footer.dom.style.display = '';
33115         for(var k in buttons){
33116             if(typeof buttons[k] != "function"){
33117                 if(b[k]){
33118                     buttons[k].show();
33119                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33120                     width += buttons[k].el.getWidth()+15;
33121                 }else{
33122                     buttons[k].hide();
33123                 }
33124             }
33125         }
33126         return width;
33127     };
33128
33129     // private
33130     var handleEsc = function(d, k, e){
33131         if(opt && opt.closable !== false){
33132             dlg.hide();
33133         }
33134         if(e){
33135             e.stopEvent();
33136         }
33137     };
33138
33139     return {
33140         /**
33141          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33142          * @return {Roo.BasicDialog} The BasicDialog element
33143          */
33144         getDialog : function(){
33145            if(!dlg){
33146                 dlg = new Roo.BasicDialog("x-msg-box", {
33147                     autoCreate : true,
33148                     shadow: true,
33149                     draggable: true,
33150                     resizable:false,
33151                     constraintoviewport:false,
33152                     fixedcenter:true,
33153                     collapsible : false,
33154                     shim:true,
33155                     modal: true,
33156                     width:400, height:100,
33157                     buttonAlign:"center",
33158                     closeClick : function(){
33159                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33160                             handleButton("no");
33161                         }else{
33162                             handleButton("cancel");
33163                         }
33164                     }
33165                 });
33166                 dlg.on("hide", handleHide);
33167                 mask = dlg.mask;
33168                 dlg.addKeyListener(27, handleEsc);
33169                 buttons = {};
33170                 var bt = this.buttonText;
33171                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33172                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33173                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33174                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33175                 bodyEl = dlg.body.createChild({
33176
33177                     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>'
33178                 });
33179                 msgEl = bodyEl.dom.firstChild;
33180                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33181                 textboxEl.enableDisplayMode();
33182                 textboxEl.addKeyListener([10,13], function(){
33183                     if(dlg.isVisible() && opt && opt.buttons){
33184                         if(opt.buttons.ok){
33185                             handleButton("ok");
33186                         }else if(opt.buttons.yes){
33187                             handleButton("yes");
33188                         }
33189                     }
33190                 });
33191                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33192                 textareaEl.enableDisplayMode();
33193                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33194                 progressEl.enableDisplayMode();
33195                 var pf = progressEl.dom.firstChild;
33196                 if (pf) {
33197                     pp = Roo.get(pf.firstChild);
33198                     pp.setHeight(pf.offsetHeight);
33199                 }
33200                 
33201             }
33202             return dlg;
33203         },
33204
33205         /**
33206          * Updates the message box body text
33207          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33208          * the XHTML-compliant non-breaking space character '&amp;#160;')
33209          * @return {Roo.MessageBox} This message box
33210          */
33211         updateText : function(text){
33212             if(!dlg.isVisible() && !opt.width){
33213                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33214             }
33215             msgEl.innerHTML = text || '&#160;';
33216       
33217             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33218             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33219             var w = Math.max(
33220                     Math.min(opt.width || cw , this.maxWidth), 
33221                     Math.max(opt.minWidth || this.minWidth, bwidth)
33222             );
33223             if(opt.prompt){
33224                 activeTextEl.setWidth(w);
33225             }
33226             if(dlg.isVisible()){
33227                 dlg.fixedcenter = false;
33228             }
33229             // to big, make it scroll. = But as usual stupid IE does not support
33230             // !important..
33231             
33232             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33233                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33234                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33235             } else {
33236                 bodyEl.dom.style.height = '';
33237                 bodyEl.dom.style.overflowY = '';
33238             }
33239             if (cw > w) {
33240                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33241             } else {
33242                 bodyEl.dom.style.overflowX = '';
33243             }
33244             
33245             dlg.setContentSize(w, bodyEl.getHeight());
33246             if(dlg.isVisible()){
33247                 dlg.fixedcenter = true;
33248             }
33249             return this;
33250         },
33251
33252         /**
33253          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33254          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33255          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33256          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33257          * @return {Roo.MessageBox} This message box
33258          */
33259         updateProgress : function(value, text){
33260             if(text){
33261                 this.updateText(text);
33262             }
33263             if (pp) { // weird bug on my firefox - for some reason this is not defined
33264                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33265             }
33266             return this;
33267         },        
33268
33269         /**
33270          * Returns true if the message box is currently displayed
33271          * @return {Boolean} True if the message box is visible, else false
33272          */
33273         isVisible : function(){
33274             return dlg && dlg.isVisible();  
33275         },
33276
33277         /**
33278          * Hides the message box if it is displayed
33279          */
33280         hide : function(){
33281             if(this.isVisible()){
33282                 dlg.hide();
33283             }  
33284         },
33285
33286         /**
33287          * Displays a new message box, or reinitializes an existing message box, based on the config options
33288          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33289          * The following config object properties are supported:
33290          * <pre>
33291 Property    Type             Description
33292 ----------  ---------------  ------------------------------------------------------------------------------------
33293 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33294                                    closes (defaults to undefined)
33295 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33296                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33297 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33298                                    progress and wait dialogs will ignore this property and always hide the
33299                                    close button as they can only be closed programmatically.
33300 cls               String           A custom CSS class to apply to the message box element
33301 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33302                                    displayed (defaults to 75)
33303 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33304                                    function will be btn (the name of the button that was clicked, if applicable,
33305                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33306                                    Progress and wait dialogs will ignore this option since they do not respond to
33307                                    user actions and can only be closed programmatically, so any required function
33308                                    should be called by the same code after it closes the dialog.
33309 icon              String           A CSS class that provides a background image to be used as an icon for
33310                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33311 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33312 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33313 modal             Boolean          False to allow user interaction with the page while the message box is
33314                                    displayed (defaults to true)
33315 msg               String           A string that will replace the existing message box body text (defaults
33316                                    to the XHTML-compliant non-breaking space character '&#160;')
33317 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33318 progress          Boolean          True to display a progress bar (defaults to false)
33319 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33320 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33321 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33322 title             String           The title text
33323 value             String           The string value to set into the active textbox element if displayed
33324 wait              Boolean          True to display a progress bar (defaults to false)
33325 width             Number           The width of the dialog in pixels
33326 </pre>
33327          *
33328          * Example usage:
33329          * <pre><code>
33330 Roo.Msg.show({
33331    title: 'Address',
33332    msg: 'Please enter your address:',
33333    width: 300,
33334    buttons: Roo.MessageBox.OKCANCEL,
33335    multiline: true,
33336    fn: saveAddress,
33337    animEl: 'addAddressBtn'
33338 });
33339 </code></pre>
33340          * @param {Object} config Configuration options
33341          * @return {Roo.MessageBox} This message box
33342          */
33343         show : function(options)
33344         {
33345             
33346             // this causes nightmares if you show one dialog after another
33347             // especially on callbacks..
33348              
33349             if(this.isVisible()){
33350                 
33351                 this.hide();
33352                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33353                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33354                 Roo.log("New Dialog Message:" +  options.msg )
33355                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33356                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33357                 
33358             }
33359             var d = this.getDialog();
33360             opt = options;
33361             d.setTitle(opt.title || "&#160;");
33362             d.close.setDisplayed(opt.closable !== false);
33363             activeTextEl = textboxEl;
33364             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33365             if(opt.prompt){
33366                 if(opt.multiline){
33367                     textboxEl.hide();
33368                     textareaEl.show();
33369                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33370                         opt.multiline : this.defaultTextHeight);
33371                     activeTextEl = textareaEl;
33372                 }else{
33373                     textboxEl.show();
33374                     textareaEl.hide();
33375                 }
33376             }else{
33377                 textboxEl.hide();
33378                 textareaEl.hide();
33379             }
33380             progressEl.setDisplayed(opt.progress === true);
33381             this.updateProgress(0);
33382             activeTextEl.dom.value = opt.value || "";
33383             if(opt.prompt){
33384                 dlg.setDefaultButton(activeTextEl);
33385             }else{
33386                 var bs = opt.buttons;
33387                 var db = null;
33388                 if(bs && bs.ok){
33389                     db = buttons["ok"];
33390                 }else if(bs && bs.yes){
33391                     db = buttons["yes"];
33392                 }
33393                 dlg.setDefaultButton(db);
33394             }
33395             bwidth = updateButtons(opt.buttons);
33396             this.updateText(opt.msg);
33397             if(opt.cls){
33398                 d.el.addClass(opt.cls);
33399             }
33400             d.proxyDrag = opt.proxyDrag === true;
33401             d.modal = opt.modal !== false;
33402             d.mask = opt.modal !== false ? mask : false;
33403             if(!d.isVisible()){
33404                 // force it to the end of the z-index stack so it gets a cursor in FF
33405                 document.body.appendChild(dlg.el.dom);
33406                 d.animateTarget = null;
33407                 d.show(options.animEl);
33408             }
33409             return this;
33410         },
33411
33412         /**
33413          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33414          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33415          * and closing the message box when the process is complete.
33416          * @param {String} title The title bar text
33417          * @param {String} msg The message box body text
33418          * @return {Roo.MessageBox} This message box
33419          */
33420         progress : function(title, msg){
33421             this.show({
33422                 title : title,
33423                 msg : msg,
33424                 buttons: false,
33425                 progress:true,
33426                 closable:false,
33427                 minWidth: this.minProgressWidth,
33428                 modal : true
33429             });
33430             return this;
33431         },
33432
33433         /**
33434          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33435          * If a callback function is passed it will be called after the user clicks the button, and the
33436          * id of the button that was clicked will be passed as the only parameter to the callback
33437          * (could also be the top-right close button).
33438          * @param {String} title The title bar text
33439          * @param {String} msg The message box body text
33440          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33441          * @param {Object} scope (optional) The scope of the callback function
33442          * @return {Roo.MessageBox} This message box
33443          */
33444         alert : function(title, msg, fn, scope){
33445             this.show({
33446                 title : title,
33447                 msg : msg,
33448                 buttons: this.OK,
33449                 fn: fn,
33450                 scope : scope,
33451                 modal : true
33452             });
33453             return this;
33454         },
33455
33456         /**
33457          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33458          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33459          * You are responsible for closing the message box when the process is complete.
33460          * @param {String} msg The message box body text
33461          * @param {String} title (optional) The title bar text
33462          * @return {Roo.MessageBox} This message box
33463          */
33464         wait : function(msg, title){
33465             this.show({
33466                 title : title,
33467                 msg : msg,
33468                 buttons: false,
33469                 closable:false,
33470                 progress:true,
33471                 modal:true,
33472                 width:300,
33473                 wait:true
33474             });
33475             waitTimer = Roo.TaskMgr.start({
33476                 run: function(i){
33477                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33478                 },
33479                 interval: 1000
33480             });
33481             return this;
33482         },
33483
33484         /**
33485          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33486          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33487          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33488          * @param {String} title The title bar text
33489          * @param {String} msg The message box body text
33490          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33491          * @param {Object} scope (optional) The scope of the callback function
33492          * @return {Roo.MessageBox} This message box
33493          */
33494         confirm : function(title, msg, fn, scope){
33495             this.show({
33496                 title : title,
33497                 msg : msg,
33498                 buttons: this.YESNO,
33499                 fn: fn,
33500                 scope : scope,
33501                 modal : true
33502             });
33503             return this;
33504         },
33505
33506         /**
33507          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33508          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33509          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33510          * (could also be the top-right close button) and the text that was entered will be passed as the two
33511          * parameters to the callback.
33512          * @param {String} title The title bar text
33513          * @param {String} msg The message box body text
33514          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33515          * @param {Object} scope (optional) The scope of the callback function
33516          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33517          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33518          * @return {Roo.MessageBox} This message box
33519          */
33520         prompt : function(title, msg, fn, scope, multiline){
33521             this.show({
33522                 title : title,
33523                 msg : msg,
33524                 buttons: this.OKCANCEL,
33525                 fn: fn,
33526                 minWidth:250,
33527                 scope : scope,
33528                 prompt:true,
33529                 multiline: multiline,
33530                 modal : true
33531             });
33532             return this;
33533         },
33534
33535         /**
33536          * Button config that displays a single OK button
33537          * @type Object
33538          */
33539         OK : {ok:true},
33540         /**
33541          * Button config that displays Yes and No buttons
33542          * @type Object
33543          */
33544         YESNO : {yes:true, no:true},
33545         /**
33546          * Button config that displays OK and Cancel buttons
33547          * @type Object
33548          */
33549         OKCANCEL : {ok:true, cancel:true},
33550         /**
33551          * Button config that displays Yes, No and Cancel buttons
33552          * @type Object
33553          */
33554         YESNOCANCEL : {yes:true, no:true, cancel:true},
33555
33556         /**
33557          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33558          * @type Number
33559          */
33560         defaultTextHeight : 75,
33561         /**
33562          * The maximum width in pixels of the message box (defaults to 600)
33563          * @type Number
33564          */
33565         maxWidth : 600,
33566         /**
33567          * The minimum width in pixels of the message box (defaults to 100)
33568          * @type Number
33569          */
33570         minWidth : 100,
33571         /**
33572          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33573          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33574          * @type Number
33575          */
33576         minProgressWidth : 250,
33577         /**
33578          * An object containing the default button text strings that can be overriden for localized language support.
33579          * Supported properties are: ok, cancel, yes and no.
33580          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33581          * @type Object
33582          */
33583         buttonText : {
33584             ok : "OK",
33585             cancel : "Cancel",
33586             yes : "Yes",
33587             no : "No"
33588         }
33589     };
33590 }();
33591
33592 /**
33593  * Shorthand for {@link Roo.MessageBox}
33594  */
33595 Roo.Msg = Roo.MessageBox;/*
33596  * Based on:
33597  * Ext JS Library 1.1.1
33598  * Copyright(c) 2006-2007, Ext JS, LLC.
33599  *
33600  * Originally Released Under LGPL - original licence link has changed is not relivant.
33601  *
33602  * Fork - LGPL
33603  * <script type="text/javascript">
33604  */
33605 /**
33606  * @class Roo.QuickTips
33607  * Provides attractive and customizable tooltips for any element.
33608  * @singleton
33609  */
33610 Roo.QuickTips = function(){
33611     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33612     var ce, bd, xy, dd;
33613     var visible = false, disabled = true, inited = false;
33614     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33615     
33616     var onOver = function(e){
33617         if(disabled){
33618             return;
33619         }
33620         var t = e.getTarget();
33621         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33622             return;
33623         }
33624         if(ce && t == ce.el){
33625             clearTimeout(hideProc);
33626             return;
33627         }
33628         if(t && tagEls[t.id]){
33629             tagEls[t.id].el = t;
33630             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33631             return;
33632         }
33633         var ttp, et = Roo.fly(t);
33634         var ns = cfg.namespace;
33635         if(tm.interceptTitles && t.title){
33636             ttp = t.title;
33637             t.qtip = ttp;
33638             t.removeAttribute("title");
33639             e.preventDefault();
33640         }else{
33641             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33642         }
33643         if(ttp){
33644             showProc = show.defer(tm.showDelay, tm, [{
33645                 el: t, 
33646                 text: ttp.replace(/\\n/g,'<br/>'),
33647                 width: et.getAttributeNS(ns, cfg.width),
33648                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33649                 title: et.getAttributeNS(ns, cfg.title),
33650                     cls: et.getAttributeNS(ns, cfg.cls)
33651             }]);
33652         }
33653     };
33654     
33655     var onOut = function(e){
33656         clearTimeout(showProc);
33657         var t = e.getTarget();
33658         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33659             hideProc = setTimeout(hide, tm.hideDelay);
33660         }
33661     };
33662     
33663     var onMove = function(e){
33664         if(disabled){
33665             return;
33666         }
33667         xy = e.getXY();
33668         xy[1] += 18;
33669         if(tm.trackMouse && ce){
33670             el.setXY(xy);
33671         }
33672     };
33673     
33674     var onDown = function(e){
33675         clearTimeout(showProc);
33676         clearTimeout(hideProc);
33677         if(!e.within(el)){
33678             if(tm.hideOnClick){
33679                 hide();
33680                 tm.disable();
33681                 tm.enable.defer(100, tm);
33682             }
33683         }
33684     };
33685     
33686     var getPad = function(){
33687         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33688     };
33689
33690     var show = function(o){
33691         if(disabled){
33692             return;
33693         }
33694         clearTimeout(dismissProc);
33695         ce = o;
33696         if(removeCls){ // in case manually hidden
33697             el.removeClass(removeCls);
33698             removeCls = null;
33699         }
33700         if(ce.cls){
33701             el.addClass(ce.cls);
33702             removeCls = ce.cls;
33703         }
33704         if(ce.title){
33705             tipTitle.update(ce.title);
33706             tipTitle.show();
33707         }else{
33708             tipTitle.update('');
33709             tipTitle.hide();
33710         }
33711         el.dom.style.width  = tm.maxWidth+'px';
33712         //tipBody.dom.style.width = '';
33713         tipBodyText.update(o.text);
33714         var p = getPad(), w = ce.width;
33715         if(!w){
33716             var td = tipBodyText.dom;
33717             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33718             if(aw > tm.maxWidth){
33719                 w = tm.maxWidth;
33720             }else if(aw < tm.minWidth){
33721                 w = tm.minWidth;
33722             }else{
33723                 w = aw;
33724             }
33725         }
33726         //tipBody.setWidth(w);
33727         el.setWidth(parseInt(w, 10) + p);
33728         if(ce.autoHide === false){
33729             close.setDisplayed(true);
33730             if(dd){
33731                 dd.unlock();
33732             }
33733         }else{
33734             close.setDisplayed(false);
33735             if(dd){
33736                 dd.lock();
33737             }
33738         }
33739         if(xy){
33740             el.avoidY = xy[1]-18;
33741             el.setXY(xy);
33742         }
33743         if(tm.animate){
33744             el.setOpacity(.1);
33745             el.setStyle("visibility", "visible");
33746             el.fadeIn({callback: afterShow});
33747         }else{
33748             afterShow();
33749         }
33750     };
33751     
33752     var afterShow = function(){
33753         if(ce){
33754             el.show();
33755             esc.enable();
33756             if(tm.autoDismiss && ce.autoHide !== false){
33757                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33758             }
33759         }
33760     };
33761     
33762     var hide = function(noanim){
33763         clearTimeout(dismissProc);
33764         clearTimeout(hideProc);
33765         ce = null;
33766         if(el.isVisible()){
33767             esc.disable();
33768             if(noanim !== true && tm.animate){
33769                 el.fadeOut({callback: afterHide});
33770             }else{
33771                 afterHide();
33772             } 
33773         }
33774     };
33775     
33776     var afterHide = function(){
33777         el.hide();
33778         if(removeCls){
33779             el.removeClass(removeCls);
33780             removeCls = null;
33781         }
33782     };
33783     
33784     return {
33785         /**
33786         * @cfg {Number} minWidth
33787         * The minimum width of the quick tip (defaults to 40)
33788         */
33789        minWidth : 40,
33790         /**
33791         * @cfg {Number} maxWidth
33792         * The maximum width of the quick tip (defaults to 300)
33793         */
33794        maxWidth : 300,
33795         /**
33796         * @cfg {Boolean} interceptTitles
33797         * True to automatically use the element's DOM title value if available (defaults to false)
33798         */
33799        interceptTitles : false,
33800         /**
33801         * @cfg {Boolean} trackMouse
33802         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33803         */
33804        trackMouse : false,
33805         /**
33806         * @cfg {Boolean} hideOnClick
33807         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33808         */
33809        hideOnClick : true,
33810         /**
33811         * @cfg {Number} showDelay
33812         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33813         */
33814        showDelay : 500,
33815         /**
33816         * @cfg {Number} hideDelay
33817         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33818         */
33819        hideDelay : 200,
33820         /**
33821         * @cfg {Boolean} autoHide
33822         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33823         * Used in conjunction with hideDelay.
33824         */
33825        autoHide : true,
33826         /**
33827         * @cfg {Boolean}
33828         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
33829         * (defaults to true).  Used in conjunction with autoDismissDelay.
33830         */
33831        autoDismiss : true,
33832         /**
33833         * @cfg {Number}
33834         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
33835         */
33836        autoDismissDelay : 5000,
33837        /**
33838         * @cfg {Boolean} animate
33839         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
33840         */
33841        animate : false,
33842
33843        /**
33844         * @cfg {String} title
33845         * Title text to display (defaults to '').  This can be any valid HTML markup.
33846         */
33847         title: '',
33848        /**
33849         * @cfg {String} text
33850         * Body text to display (defaults to '').  This can be any valid HTML markup.
33851         */
33852         text : '',
33853        /**
33854         * @cfg {String} cls
33855         * A CSS class to apply to the base quick tip element (defaults to '').
33856         */
33857         cls : '',
33858        /**
33859         * @cfg {Number} width
33860         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
33861         * minWidth or maxWidth.
33862         */
33863         width : null,
33864
33865     /**
33866      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
33867      * or display QuickTips in a page.
33868      */
33869        init : function(){
33870           tm = Roo.QuickTips;
33871           cfg = tm.tagConfig;
33872           if(!inited){
33873               if(!Roo.isReady){ // allow calling of init() before onReady
33874                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
33875                   return;
33876               }
33877               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
33878               el.fxDefaults = {stopFx: true};
33879               // maximum custom styling
33880               //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>');
33881               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>');              
33882               tipTitle = el.child('h3');
33883               tipTitle.enableDisplayMode("block");
33884               tipBody = el.child('div.x-tip-bd');
33885               tipBodyText = el.child('div.x-tip-bd-inner');
33886               //bdLeft = el.child('div.x-tip-bd-left');
33887               //bdRight = el.child('div.x-tip-bd-right');
33888               close = el.child('div.x-tip-close');
33889               close.enableDisplayMode("block");
33890               close.on("click", hide);
33891               var d = Roo.get(document);
33892               d.on("mousedown", onDown);
33893               d.on("mouseover", onOver);
33894               d.on("mouseout", onOut);
33895               d.on("mousemove", onMove);
33896               esc = d.addKeyListener(27, hide);
33897               esc.disable();
33898               if(Roo.dd.DD){
33899                   dd = el.initDD("default", null, {
33900                       onDrag : function(){
33901                           el.sync();  
33902                       }
33903                   });
33904                   dd.setHandleElId(tipTitle.id);
33905                   dd.lock();
33906               }
33907               inited = true;
33908           }
33909           this.enable(); 
33910        },
33911
33912     /**
33913      * Configures a new quick tip instance and assigns it to a target element.  The following config options
33914      * are supported:
33915      * <pre>
33916 Property    Type                   Description
33917 ----------  ---------------------  ------------------------------------------------------------------------
33918 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
33919      * </ul>
33920      * @param {Object} config The config object
33921      */
33922        register : function(config){
33923            var cs = config instanceof Array ? config : arguments;
33924            for(var i = 0, len = cs.length; i < len; i++) {
33925                var c = cs[i];
33926                var target = c.target;
33927                if(target){
33928                    if(target instanceof Array){
33929                        for(var j = 0, jlen = target.length; j < jlen; j++){
33930                            tagEls[target[j]] = c;
33931                        }
33932                    }else{
33933                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
33934                    }
33935                }
33936            }
33937        },
33938
33939     /**
33940      * Removes this quick tip from its element and destroys it.
33941      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
33942      */
33943        unregister : function(el){
33944            delete tagEls[Roo.id(el)];
33945        },
33946
33947     /**
33948      * Enable this quick tip.
33949      */
33950        enable : function(){
33951            if(inited && disabled){
33952                locks.pop();
33953                if(locks.length < 1){
33954                    disabled = false;
33955                }
33956            }
33957        },
33958
33959     /**
33960      * Disable this quick tip.
33961      */
33962        disable : function(){
33963           disabled = true;
33964           clearTimeout(showProc);
33965           clearTimeout(hideProc);
33966           clearTimeout(dismissProc);
33967           if(ce){
33968               hide(true);
33969           }
33970           locks.push(1);
33971        },
33972
33973     /**
33974      * Returns true if the quick tip is enabled, else false.
33975      */
33976        isEnabled : function(){
33977             return !disabled;
33978        },
33979
33980         // private
33981        tagConfig : {
33982            namespace : "roo", // was ext?? this may break..
33983            alt_namespace : "ext",
33984            attribute : "qtip",
33985            width : "width",
33986            target : "target",
33987            title : "qtitle",
33988            hide : "hide",
33989            cls : "qclass"
33990        }
33991    };
33992 }();
33993
33994 // backwards compat
33995 Roo.QuickTips.tips = Roo.QuickTips.register;/*
33996  * Based on:
33997  * Ext JS Library 1.1.1
33998  * Copyright(c) 2006-2007, Ext JS, LLC.
33999  *
34000  * Originally Released Under LGPL - original licence link has changed is not relivant.
34001  *
34002  * Fork - LGPL
34003  * <script type="text/javascript">
34004  */
34005  
34006
34007 /**
34008  * @class Roo.tree.TreePanel
34009  * @extends Roo.data.Tree
34010
34011  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
34012  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
34013  * @cfg {Boolean} enableDD true to enable drag and drop
34014  * @cfg {Boolean} enableDrag true to enable just drag
34015  * @cfg {Boolean} enableDrop true to enable just drop
34016  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
34017  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
34018  * @cfg {String} ddGroup The DD group this TreePanel belongs to
34019  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
34020  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
34021  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
34022  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
34023  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
34024  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
34025  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
34026  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
34027  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
34028  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
34029  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
34030  * @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>
34031  * @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>
34032  * 
34033  * @constructor
34034  * @param {String/HTMLElement/Element} el The container element
34035  * @param {Object} config
34036  */
34037 Roo.tree.TreePanel = function(el, config){
34038     var root = false;
34039     var loader = false;
34040     if (config.root) {
34041         root = config.root;
34042         delete config.root;
34043     }
34044     if (config.loader) {
34045         loader = config.loader;
34046         delete config.loader;
34047     }
34048     
34049     Roo.apply(this, config);
34050     Roo.tree.TreePanel.superclass.constructor.call(this);
34051     this.el = Roo.get(el);
34052     this.el.addClass('x-tree');
34053     //console.log(root);
34054     if (root) {
34055         this.setRootNode( Roo.factory(root, Roo.tree));
34056     }
34057     if (loader) {
34058         this.loader = Roo.factory(loader, Roo.tree);
34059     }
34060    /**
34061     * Read-only. The id of the container element becomes this TreePanel's id.
34062     */
34063     this.id = this.el.id;
34064     this.addEvents({
34065         /**
34066         * @event beforeload
34067         * Fires before a node is loaded, return false to cancel
34068         * @param {Node} node The node being loaded
34069         */
34070         "beforeload" : true,
34071         /**
34072         * @event load
34073         * Fires when a node is loaded
34074         * @param {Node} node The node that was loaded
34075         */
34076         "load" : true,
34077         /**
34078         * @event textchange
34079         * Fires when the text for a node is changed
34080         * @param {Node} node The node
34081         * @param {String} text The new text
34082         * @param {String} oldText The old text
34083         */
34084         "textchange" : true,
34085         /**
34086         * @event beforeexpand
34087         * Fires before a node is expanded, return false to cancel.
34088         * @param {Node} node The node
34089         * @param {Boolean} deep
34090         * @param {Boolean} anim
34091         */
34092         "beforeexpand" : true,
34093         /**
34094         * @event beforecollapse
34095         * Fires before a node is collapsed, return false to cancel.
34096         * @param {Node} node The node
34097         * @param {Boolean} deep
34098         * @param {Boolean} anim
34099         */
34100         "beforecollapse" : true,
34101         /**
34102         * @event expand
34103         * Fires when a node is expanded
34104         * @param {Node} node The node
34105         */
34106         "expand" : true,
34107         /**
34108         * @event disabledchange
34109         * Fires when the disabled status of a node changes
34110         * @param {Node} node The node
34111         * @param {Boolean} disabled
34112         */
34113         "disabledchange" : true,
34114         /**
34115         * @event collapse
34116         * Fires when a node is collapsed
34117         * @param {Node} node The node
34118         */
34119         "collapse" : true,
34120         /**
34121         * @event beforeclick
34122         * Fires before click processing on a node. Return false to cancel the default action.
34123         * @param {Node} node The node
34124         * @param {Roo.EventObject} e The event object
34125         */
34126         "beforeclick":true,
34127         /**
34128         * @event checkchange
34129         * Fires when a node with a checkbox's checked property changes
34130         * @param {Node} this This node
34131         * @param {Boolean} checked
34132         */
34133         "checkchange":true,
34134         /**
34135         * @event click
34136         * Fires when a node is clicked
34137         * @param {Node} node The node
34138         * @param {Roo.EventObject} e The event object
34139         */
34140         "click":true,
34141         /**
34142         * @event dblclick
34143         * Fires when a node is double clicked
34144         * @param {Node} node The node
34145         * @param {Roo.EventObject} e The event object
34146         */
34147         "dblclick":true,
34148         /**
34149         * @event contextmenu
34150         * Fires when a node is right clicked
34151         * @param {Node} node The node
34152         * @param {Roo.EventObject} e The event object
34153         */
34154         "contextmenu":true,
34155         /**
34156         * @event beforechildrenrendered
34157         * Fires right before the child nodes for a node are rendered
34158         * @param {Node} node The node
34159         */
34160         "beforechildrenrendered":true,
34161         /**
34162         * @event startdrag
34163         * Fires when a node starts being dragged
34164         * @param {Roo.tree.TreePanel} this
34165         * @param {Roo.tree.TreeNode} node
34166         * @param {event} e The raw browser event
34167         */ 
34168        "startdrag" : true,
34169        /**
34170         * @event enddrag
34171         * Fires when a drag operation is complete
34172         * @param {Roo.tree.TreePanel} this
34173         * @param {Roo.tree.TreeNode} node
34174         * @param {event} e The raw browser event
34175         */
34176        "enddrag" : true,
34177        /**
34178         * @event dragdrop
34179         * Fires when a dragged node is dropped on a valid DD target
34180         * @param {Roo.tree.TreePanel} this
34181         * @param {Roo.tree.TreeNode} node
34182         * @param {DD} dd The dd it was dropped on
34183         * @param {event} e The raw browser event
34184         */
34185        "dragdrop" : true,
34186        /**
34187         * @event beforenodedrop
34188         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34189         * passed to handlers has the following properties:<br />
34190         * <ul style="padding:5px;padding-left:16px;">
34191         * <li>tree - The TreePanel</li>
34192         * <li>target - The node being targeted for the drop</li>
34193         * <li>data - The drag data from the drag source</li>
34194         * <li>point - The point of the drop - append, above or below</li>
34195         * <li>source - The drag source</li>
34196         * <li>rawEvent - Raw mouse event</li>
34197         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34198         * to be inserted by setting them on this object.</li>
34199         * <li>cancel - Set this to true to cancel the drop.</li>
34200         * </ul>
34201         * @param {Object} dropEvent
34202         */
34203        "beforenodedrop" : true,
34204        /**
34205         * @event nodedrop
34206         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34207         * passed to handlers has the following properties:<br />
34208         * <ul style="padding:5px;padding-left:16px;">
34209         * <li>tree - The TreePanel</li>
34210         * <li>target - The node being targeted for the drop</li>
34211         * <li>data - The drag data from the drag source</li>
34212         * <li>point - The point of the drop - append, above or below</li>
34213         * <li>source - The drag source</li>
34214         * <li>rawEvent - Raw mouse event</li>
34215         * <li>dropNode - Dropped node(s).</li>
34216         * </ul>
34217         * @param {Object} dropEvent
34218         */
34219        "nodedrop" : true,
34220         /**
34221         * @event nodedragover
34222         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34223         * passed to handlers has the following properties:<br />
34224         * <ul style="padding:5px;padding-left:16px;">
34225         * <li>tree - The TreePanel</li>
34226         * <li>target - The node being targeted for the drop</li>
34227         * <li>data - The drag data from the drag source</li>
34228         * <li>point - The point of the drop - append, above or below</li>
34229         * <li>source - The drag source</li>
34230         * <li>rawEvent - Raw mouse event</li>
34231         * <li>dropNode - Drop node(s) provided by the source.</li>
34232         * <li>cancel - Set this to true to signal drop not allowed.</li>
34233         * </ul>
34234         * @param {Object} dragOverEvent
34235         */
34236        "nodedragover" : true
34237         
34238     });
34239     if(this.singleExpand){
34240        this.on("beforeexpand", this.restrictExpand, this);
34241     }
34242     if (this.editor) {
34243         this.editor.tree = this;
34244         this.editor = Roo.factory(this.editor, Roo.tree);
34245     }
34246     
34247     if (this.selModel) {
34248         this.selModel = Roo.factory(this.selModel, Roo.tree);
34249     }
34250    
34251 };
34252 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34253     rootVisible : true,
34254     animate: Roo.enableFx,
34255     lines : true,
34256     enableDD : false,
34257     hlDrop : Roo.enableFx,
34258   
34259     renderer: false,
34260     
34261     rendererTip: false,
34262     // private
34263     restrictExpand : function(node){
34264         var p = node.parentNode;
34265         if(p){
34266             if(p.expandedChild && p.expandedChild.parentNode == p){
34267                 p.expandedChild.collapse();
34268             }
34269             p.expandedChild = node;
34270         }
34271     },
34272
34273     // private override
34274     setRootNode : function(node){
34275         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34276         if(!this.rootVisible){
34277             node.ui = new Roo.tree.RootTreeNodeUI(node);
34278         }
34279         return node;
34280     },
34281
34282     /**
34283      * Returns the container element for this TreePanel
34284      */
34285     getEl : function(){
34286         return this.el;
34287     },
34288
34289     /**
34290      * Returns the default TreeLoader for this TreePanel
34291      */
34292     getLoader : function(){
34293         return this.loader;
34294     },
34295
34296     /**
34297      * Expand all nodes
34298      */
34299     expandAll : function(){
34300         this.root.expand(true);
34301     },
34302
34303     /**
34304      * Collapse all nodes
34305      */
34306     collapseAll : function(){
34307         this.root.collapse(true);
34308     },
34309
34310     /**
34311      * Returns the selection model used by this TreePanel
34312      */
34313     getSelectionModel : function(){
34314         if(!this.selModel){
34315             this.selModel = new Roo.tree.DefaultSelectionModel();
34316         }
34317         return this.selModel;
34318     },
34319
34320     /**
34321      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34322      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34323      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34324      * @return {Array}
34325      */
34326     getChecked : function(a, startNode){
34327         startNode = startNode || this.root;
34328         var r = [];
34329         var f = function(){
34330             if(this.attributes.checked){
34331                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34332             }
34333         }
34334         startNode.cascade(f);
34335         return r;
34336     },
34337
34338     /**
34339      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34340      * @param {String} path
34341      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34342      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34343      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34344      */
34345     expandPath : function(path, attr, callback){
34346         attr = attr || "id";
34347         var keys = path.split(this.pathSeparator);
34348         var curNode = this.root;
34349         if(curNode.attributes[attr] != keys[1]){ // invalid root
34350             if(callback){
34351                 callback(false, null);
34352             }
34353             return;
34354         }
34355         var index = 1;
34356         var f = function(){
34357             if(++index == keys.length){
34358                 if(callback){
34359                     callback(true, curNode);
34360                 }
34361                 return;
34362             }
34363             var c = curNode.findChild(attr, keys[index]);
34364             if(!c){
34365                 if(callback){
34366                     callback(false, curNode);
34367                 }
34368                 return;
34369             }
34370             curNode = c;
34371             c.expand(false, false, f);
34372         };
34373         curNode.expand(false, false, f);
34374     },
34375
34376     /**
34377      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34378      * @param {String} path
34379      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34380      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34381      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34382      */
34383     selectPath : function(path, attr, callback){
34384         attr = attr || "id";
34385         var keys = path.split(this.pathSeparator);
34386         var v = keys.pop();
34387         if(keys.length > 0){
34388             var f = function(success, node){
34389                 if(success && node){
34390                     var n = node.findChild(attr, v);
34391                     if(n){
34392                         n.select();
34393                         if(callback){
34394                             callback(true, n);
34395                         }
34396                     }else if(callback){
34397                         callback(false, n);
34398                     }
34399                 }else{
34400                     if(callback){
34401                         callback(false, n);
34402                     }
34403                 }
34404             };
34405             this.expandPath(keys.join(this.pathSeparator), attr, f);
34406         }else{
34407             this.root.select();
34408             if(callback){
34409                 callback(true, this.root);
34410             }
34411         }
34412     },
34413
34414     getTreeEl : function(){
34415         return this.el;
34416     },
34417
34418     /**
34419      * Trigger rendering of this TreePanel
34420      */
34421     render : function(){
34422         if (this.innerCt) {
34423             return this; // stop it rendering more than once!!
34424         }
34425         
34426         this.innerCt = this.el.createChild({tag:"ul",
34427                cls:"x-tree-root-ct " +
34428                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34429
34430         if(this.containerScroll){
34431             Roo.dd.ScrollManager.register(this.el);
34432         }
34433         if((this.enableDD || this.enableDrop) && !this.dropZone){
34434            /**
34435             * The dropZone used by this tree if drop is enabled
34436             * @type Roo.tree.TreeDropZone
34437             */
34438              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34439                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34440            });
34441         }
34442         if((this.enableDD || this.enableDrag) && !this.dragZone){
34443            /**
34444             * The dragZone used by this tree if drag is enabled
34445             * @type Roo.tree.TreeDragZone
34446             */
34447             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34448                ddGroup: this.ddGroup || "TreeDD",
34449                scroll: this.ddScroll
34450            });
34451         }
34452         this.getSelectionModel().init(this);
34453         if (!this.root) {
34454             Roo.log("ROOT not set in tree");
34455             return this;
34456         }
34457         this.root.render();
34458         if(!this.rootVisible){
34459             this.root.renderChildren();
34460         }
34461         return this;
34462     }
34463 });/*
34464  * Based on:
34465  * Ext JS Library 1.1.1
34466  * Copyright(c) 2006-2007, Ext JS, LLC.
34467  *
34468  * Originally Released Under LGPL - original licence link has changed is not relivant.
34469  *
34470  * Fork - LGPL
34471  * <script type="text/javascript">
34472  */
34473  
34474
34475 /**
34476  * @class Roo.tree.DefaultSelectionModel
34477  * @extends Roo.util.Observable
34478  * The default single selection for a TreePanel.
34479  * @param {Object} cfg Configuration
34480  */
34481 Roo.tree.DefaultSelectionModel = function(cfg){
34482    this.selNode = null;
34483    
34484    
34485    
34486    this.addEvents({
34487        /**
34488         * @event selectionchange
34489         * Fires when the selected node changes
34490         * @param {DefaultSelectionModel} this
34491         * @param {TreeNode} node the new selection
34492         */
34493        "selectionchange" : true,
34494
34495        /**
34496         * @event beforeselect
34497         * Fires before the selected node changes, return false to cancel the change
34498         * @param {DefaultSelectionModel} this
34499         * @param {TreeNode} node the new selection
34500         * @param {TreeNode} node the old selection
34501         */
34502        "beforeselect" : true
34503    });
34504    
34505     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34506 };
34507
34508 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34509     init : function(tree){
34510         this.tree = tree;
34511         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34512         tree.on("click", this.onNodeClick, this);
34513     },
34514     
34515     onNodeClick : function(node, e){
34516         if (e.ctrlKey && this.selNode == node)  {
34517             this.unselect(node);
34518             return;
34519         }
34520         this.select(node);
34521     },
34522     
34523     /**
34524      * Select a node.
34525      * @param {TreeNode} node The node to select
34526      * @return {TreeNode} The selected node
34527      */
34528     select : function(node){
34529         var last = this.selNode;
34530         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34531             if(last){
34532                 last.ui.onSelectedChange(false);
34533             }
34534             this.selNode = node;
34535             node.ui.onSelectedChange(true);
34536             this.fireEvent("selectionchange", this, node, last);
34537         }
34538         return node;
34539     },
34540     
34541     /**
34542      * Deselect a node.
34543      * @param {TreeNode} node The node to unselect
34544      */
34545     unselect : function(node){
34546         if(this.selNode == node){
34547             this.clearSelections();
34548         }    
34549     },
34550     
34551     /**
34552      * Clear all selections
34553      */
34554     clearSelections : function(){
34555         var n = this.selNode;
34556         if(n){
34557             n.ui.onSelectedChange(false);
34558             this.selNode = null;
34559             this.fireEvent("selectionchange", this, null);
34560         }
34561         return n;
34562     },
34563     
34564     /**
34565      * Get the selected node
34566      * @return {TreeNode} The selected node
34567      */
34568     getSelectedNode : function(){
34569         return this.selNode;    
34570     },
34571     
34572     /**
34573      * Returns true if the node is selected
34574      * @param {TreeNode} node The node to check
34575      * @return {Boolean}
34576      */
34577     isSelected : function(node){
34578         return this.selNode == node;  
34579     },
34580
34581     /**
34582      * Selects the node above the selected node in the tree, intelligently walking the nodes
34583      * @return TreeNode The new selection
34584      */
34585     selectPrevious : function(){
34586         var s = this.selNode || this.lastSelNode;
34587         if(!s){
34588             return null;
34589         }
34590         var ps = s.previousSibling;
34591         if(ps){
34592             if(!ps.isExpanded() || ps.childNodes.length < 1){
34593                 return this.select(ps);
34594             } else{
34595                 var lc = ps.lastChild;
34596                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34597                     lc = lc.lastChild;
34598                 }
34599                 return this.select(lc);
34600             }
34601         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34602             return this.select(s.parentNode);
34603         }
34604         return null;
34605     },
34606
34607     /**
34608      * Selects the node above the selected node in the tree, intelligently walking the nodes
34609      * @return TreeNode The new selection
34610      */
34611     selectNext : function(){
34612         var s = this.selNode || this.lastSelNode;
34613         if(!s){
34614             return null;
34615         }
34616         if(s.firstChild && s.isExpanded()){
34617              return this.select(s.firstChild);
34618          }else if(s.nextSibling){
34619              return this.select(s.nextSibling);
34620          }else if(s.parentNode){
34621             var newS = null;
34622             s.parentNode.bubble(function(){
34623                 if(this.nextSibling){
34624                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34625                     return false;
34626                 }
34627             });
34628             return newS;
34629          }
34630         return null;
34631     },
34632
34633     onKeyDown : function(e){
34634         var s = this.selNode || this.lastSelNode;
34635         // undesirable, but required
34636         var sm = this;
34637         if(!s){
34638             return;
34639         }
34640         var k = e.getKey();
34641         switch(k){
34642              case e.DOWN:
34643                  e.stopEvent();
34644                  this.selectNext();
34645              break;
34646              case e.UP:
34647                  e.stopEvent();
34648                  this.selectPrevious();
34649              break;
34650              case e.RIGHT:
34651                  e.preventDefault();
34652                  if(s.hasChildNodes()){
34653                      if(!s.isExpanded()){
34654                          s.expand();
34655                      }else if(s.firstChild){
34656                          this.select(s.firstChild, e);
34657                      }
34658                  }
34659              break;
34660              case e.LEFT:
34661                  e.preventDefault();
34662                  if(s.hasChildNodes() && s.isExpanded()){
34663                      s.collapse();
34664                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34665                      this.select(s.parentNode, e);
34666                  }
34667              break;
34668         };
34669     }
34670 });
34671
34672 /**
34673  * @class Roo.tree.MultiSelectionModel
34674  * @extends Roo.util.Observable
34675  * Multi selection for a TreePanel.
34676  * @param {Object} cfg Configuration
34677  */
34678 Roo.tree.MultiSelectionModel = function(){
34679    this.selNodes = [];
34680    this.selMap = {};
34681    this.addEvents({
34682        /**
34683         * @event selectionchange
34684         * Fires when the selected nodes change
34685         * @param {MultiSelectionModel} this
34686         * @param {Array} nodes Array of the selected nodes
34687         */
34688        "selectionchange" : true
34689    });
34690    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34691    
34692 };
34693
34694 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34695     init : function(tree){
34696         this.tree = tree;
34697         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34698         tree.on("click", this.onNodeClick, this);
34699     },
34700     
34701     onNodeClick : function(node, e){
34702         this.select(node, e, e.ctrlKey);
34703     },
34704     
34705     /**
34706      * Select a node.
34707      * @param {TreeNode} node The node to select
34708      * @param {EventObject} e (optional) An event associated with the selection
34709      * @param {Boolean} keepExisting True to retain existing selections
34710      * @return {TreeNode} The selected node
34711      */
34712     select : function(node, e, keepExisting){
34713         if(keepExisting !== true){
34714             this.clearSelections(true);
34715         }
34716         if(this.isSelected(node)){
34717             this.lastSelNode = node;
34718             return node;
34719         }
34720         this.selNodes.push(node);
34721         this.selMap[node.id] = node;
34722         this.lastSelNode = node;
34723         node.ui.onSelectedChange(true);
34724         this.fireEvent("selectionchange", this, this.selNodes);
34725         return node;
34726     },
34727     
34728     /**
34729      * Deselect a node.
34730      * @param {TreeNode} node The node to unselect
34731      */
34732     unselect : function(node){
34733         if(this.selMap[node.id]){
34734             node.ui.onSelectedChange(false);
34735             var sn = this.selNodes;
34736             var index = -1;
34737             if(sn.indexOf){
34738                 index = sn.indexOf(node);
34739             }else{
34740                 for(var i = 0, len = sn.length; i < len; i++){
34741                     if(sn[i] == node){
34742                         index = i;
34743                         break;
34744                     }
34745                 }
34746             }
34747             if(index != -1){
34748                 this.selNodes.splice(index, 1);
34749             }
34750             delete this.selMap[node.id];
34751             this.fireEvent("selectionchange", this, this.selNodes);
34752         }
34753     },
34754     
34755     /**
34756      * Clear all selections
34757      */
34758     clearSelections : function(suppressEvent){
34759         var sn = this.selNodes;
34760         if(sn.length > 0){
34761             for(var i = 0, len = sn.length; i < len; i++){
34762                 sn[i].ui.onSelectedChange(false);
34763             }
34764             this.selNodes = [];
34765             this.selMap = {};
34766             if(suppressEvent !== true){
34767                 this.fireEvent("selectionchange", this, this.selNodes);
34768             }
34769         }
34770     },
34771     
34772     /**
34773      * Returns true if the node is selected
34774      * @param {TreeNode} node The node to check
34775      * @return {Boolean}
34776      */
34777     isSelected : function(node){
34778         return this.selMap[node.id] ? true : false;  
34779     },
34780     
34781     /**
34782      * Returns an array of the selected nodes
34783      * @return {Array}
34784      */
34785     getSelectedNodes : function(){
34786         return this.selNodes;    
34787     },
34788
34789     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34790
34791     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34792
34793     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34794 });/*
34795  * Based on:
34796  * Ext JS Library 1.1.1
34797  * Copyright(c) 2006-2007, Ext JS, LLC.
34798  *
34799  * Originally Released Under LGPL - original licence link has changed is not relivant.
34800  *
34801  * Fork - LGPL
34802  * <script type="text/javascript">
34803  */
34804  
34805 /**
34806  * @class Roo.tree.TreeNode
34807  * @extends Roo.data.Node
34808  * @cfg {String} text The text for this node
34809  * @cfg {Boolean} expanded true to start the node expanded
34810  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34811  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34812  * @cfg {Boolean} disabled true to start the node disabled
34813  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34814  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
34815  * @cfg {String} cls A css class to be added to the node
34816  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34817  * @cfg {String} href URL of the link used for the node (defaults to #)
34818  * @cfg {String} hrefTarget target frame for the link
34819  * @cfg {String} qtip An Ext QuickTip for the node
34820  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
34821  * @cfg {Boolean} singleClickExpand True for single click expand on this node
34822  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
34823  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
34824  * (defaults to undefined with no checkbox rendered)
34825  * @constructor
34826  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
34827  */
34828 Roo.tree.TreeNode = function(attributes){
34829     attributes = attributes || {};
34830     if(typeof attributes == "string"){
34831         attributes = {text: attributes};
34832     }
34833     this.childrenRendered = false;
34834     this.rendered = false;
34835     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
34836     this.expanded = attributes.expanded === true;
34837     this.isTarget = attributes.isTarget !== false;
34838     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
34839     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
34840
34841     /**
34842      * Read-only. The text for this node. To change it use setText().
34843      * @type String
34844      */
34845     this.text = attributes.text;
34846     /**
34847      * True if this node is disabled.
34848      * @type Boolean
34849      */
34850     this.disabled = attributes.disabled === true;
34851
34852     this.addEvents({
34853         /**
34854         * @event textchange
34855         * Fires when the text for this node is changed
34856         * @param {Node} this This node
34857         * @param {String} text The new text
34858         * @param {String} oldText The old text
34859         */
34860         "textchange" : true,
34861         /**
34862         * @event beforeexpand
34863         * Fires before this node is expanded, return false to cancel.
34864         * @param {Node} this This node
34865         * @param {Boolean} deep
34866         * @param {Boolean} anim
34867         */
34868         "beforeexpand" : true,
34869         /**
34870         * @event beforecollapse
34871         * Fires before this node is collapsed, return false to cancel.
34872         * @param {Node} this This node
34873         * @param {Boolean} deep
34874         * @param {Boolean} anim
34875         */
34876         "beforecollapse" : true,
34877         /**
34878         * @event expand
34879         * Fires when this node is expanded
34880         * @param {Node} this This node
34881         */
34882         "expand" : true,
34883         /**
34884         * @event disabledchange
34885         * Fires when the disabled status of this node changes
34886         * @param {Node} this This node
34887         * @param {Boolean} disabled
34888         */
34889         "disabledchange" : true,
34890         /**
34891         * @event collapse
34892         * Fires when this node is collapsed
34893         * @param {Node} this This node
34894         */
34895         "collapse" : true,
34896         /**
34897         * @event beforeclick
34898         * Fires before click processing. Return false to cancel the default action.
34899         * @param {Node} this This node
34900         * @param {Roo.EventObject} e The event object
34901         */
34902         "beforeclick":true,
34903         /**
34904         * @event checkchange
34905         * Fires when a node with a checkbox's checked property changes
34906         * @param {Node} this This node
34907         * @param {Boolean} checked
34908         */
34909         "checkchange":true,
34910         /**
34911         * @event click
34912         * Fires when this node is clicked
34913         * @param {Node} this This node
34914         * @param {Roo.EventObject} e The event object
34915         */
34916         "click":true,
34917         /**
34918         * @event dblclick
34919         * Fires when this node is double clicked
34920         * @param {Node} this This node
34921         * @param {Roo.EventObject} e The event object
34922         */
34923         "dblclick":true,
34924         /**
34925         * @event contextmenu
34926         * Fires when this node is right clicked
34927         * @param {Node} this This node
34928         * @param {Roo.EventObject} e The event object
34929         */
34930         "contextmenu":true,
34931         /**
34932         * @event beforechildrenrendered
34933         * Fires right before the child nodes for this node are rendered
34934         * @param {Node} this This node
34935         */
34936         "beforechildrenrendered":true
34937     });
34938
34939     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
34940
34941     /**
34942      * Read-only. The UI for this node
34943      * @type TreeNodeUI
34944      */
34945     this.ui = new uiClass(this);
34946     
34947     // finally support items[]
34948     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
34949         return;
34950     }
34951     
34952     
34953     Roo.each(this.attributes.items, function(c) {
34954         this.appendChild(Roo.factory(c,Roo.Tree));
34955     }, this);
34956     delete this.attributes.items;
34957     
34958     
34959     
34960 };
34961 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
34962     preventHScroll: true,
34963     /**
34964      * Returns true if this node is expanded
34965      * @return {Boolean}
34966      */
34967     isExpanded : function(){
34968         return this.expanded;
34969     },
34970
34971     /**
34972      * Returns the UI object for this node
34973      * @return {TreeNodeUI}
34974      */
34975     getUI : function(){
34976         return this.ui;
34977     },
34978
34979     // private override
34980     setFirstChild : function(node){
34981         var of = this.firstChild;
34982         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
34983         if(this.childrenRendered && of && node != of){
34984             of.renderIndent(true, true);
34985         }
34986         if(this.rendered){
34987             this.renderIndent(true, true);
34988         }
34989     },
34990
34991     // private override
34992     setLastChild : function(node){
34993         var ol = this.lastChild;
34994         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
34995         if(this.childrenRendered && ol && node != ol){
34996             ol.renderIndent(true, true);
34997         }
34998         if(this.rendered){
34999             this.renderIndent(true, true);
35000         }
35001     },
35002
35003     // these methods are overridden to provide lazy rendering support
35004     // private override
35005     appendChild : function()
35006     {
35007         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
35008         if(node && this.childrenRendered){
35009             node.render();
35010         }
35011         this.ui.updateExpandIcon();
35012         return node;
35013     },
35014
35015     // private override
35016     removeChild : function(node){
35017         this.ownerTree.getSelectionModel().unselect(node);
35018         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
35019         // if it's been rendered remove dom node
35020         if(this.childrenRendered){
35021             node.ui.remove();
35022         }
35023         if(this.childNodes.length < 1){
35024             this.collapse(false, false);
35025         }else{
35026             this.ui.updateExpandIcon();
35027         }
35028         if(!this.firstChild) {
35029             this.childrenRendered = false;
35030         }
35031         return node;
35032     },
35033
35034     // private override
35035     insertBefore : function(node, refNode){
35036         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
35037         if(newNode && refNode && this.childrenRendered){
35038             node.render();
35039         }
35040         this.ui.updateExpandIcon();
35041         return newNode;
35042     },
35043
35044     /**
35045      * Sets the text for this node
35046      * @param {String} text
35047      */
35048     setText : function(text){
35049         var oldText = this.text;
35050         this.text = text;
35051         this.attributes.text = text;
35052         if(this.rendered){ // event without subscribing
35053             this.ui.onTextChange(this, text, oldText);
35054         }
35055         this.fireEvent("textchange", this, text, oldText);
35056     },
35057
35058     /**
35059      * Triggers selection of this node
35060      */
35061     select : function(){
35062         this.getOwnerTree().getSelectionModel().select(this);
35063     },
35064
35065     /**
35066      * Triggers deselection of this node
35067      */
35068     unselect : function(){
35069         this.getOwnerTree().getSelectionModel().unselect(this);
35070     },
35071
35072     /**
35073      * Returns true if this node is selected
35074      * @return {Boolean}
35075      */
35076     isSelected : function(){
35077         return this.getOwnerTree().getSelectionModel().isSelected(this);
35078     },
35079
35080     /**
35081      * Expand this node.
35082      * @param {Boolean} deep (optional) True to expand all children as well
35083      * @param {Boolean} anim (optional) false to cancel the default animation
35084      * @param {Function} callback (optional) A callback to be called when
35085      * expanding this node completes (does not wait for deep expand to complete).
35086      * Called with 1 parameter, this node.
35087      */
35088     expand : function(deep, anim, callback){
35089         if(!this.expanded){
35090             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35091                 return;
35092             }
35093             if(!this.childrenRendered){
35094                 this.renderChildren();
35095             }
35096             this.expanded = true;
35097             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
35098                 this.ui.animExpand(function(){
35099                     this.fireEvent("expand", this);
35100                     if(typeof callback == "function"){
35101                         callback(this);
35102                     }
35103                     if(deep === true){
35104                         this.expandChildNodes(true);
35105                     }
35106                 }.createDelegate(this));
35107                 return;
35108             }else{
35109                 this.ui.expand();
35110                 this.fireEvent("expand", this);
35111                 if(typeof callback == "function"){
35112                     callback(this);
35113                 }
35114             }
35115         }else{
35116            if(typeof callback == "function"){
35117                callback(this);
35118            }
35119         }
35120         if(deep === true){
35121             this.expandChildNodes(true);
35122         }
35123     },
35124
35125     isHiddenRoot : function(){
35126         return this.isRoot && !this.getOwnerTree().rootVisible;
35127     },
35128
35129     /**
35130      * Collapse this node.
35131      * @param {Boolean} deep (optional) True to collapse all children as well
35132      * @param {Boolean} anim (optional) false to cancel the default animation
35133      */
35134     collapse : function(deep, anim){
35135         if(this.expanded && !this.isHiddenRoot()){
35136             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35137                 return;
35138             }
35139             this.expanded = false;
35140             if((this.getOwnerTree().animate && anim !== false) || anim){
35141                 this.ui.animCollapse(function(){
35142                     this.fireEvent("collapse", this);
35143                     if(deep === true){
35144                         this.collapseChildNodes(true);
35145                     }
35146                 }.createDelegate(this));
35147                 return;
35148             }else{
35149                 this.ui.collapse();
35150                 this.fireEvent("collapse", this);
35151             }
35152         }
35153         if(deep === true){
35154             var cs = this.childNodes;
35155             for(var i = 0, len = cs.length; i < len; i++) {
35156                 cs[i].collapse(true, false);
35157             }
35158         }
35159     },
35160
35161     // private
35162     delayedExpand : function(delay){
35163         if(!this.expandProcId){
35164             this.expandProcId = this.expand.defer(delay, this);
35165         }
35166     },
35167
35168     // private
35169     cancelExpand : function(){
35170         if(this.expandProcId){
35171             clearTimeout(this.expandProcId);
35172         }
35173         this.expandProcId = false;
35174     },
35175
35176     /**
35177      * Toggles expanded/collapsed state of the node
35178      */
35179     toggle : function(){
35180         if(this.expanded){
35181             this.collapse();
35182         }else{
35183             this.expand();
35184         }
35185     },
35186
35187     /**
35188      * Ensures all parent nodes are expanded
35189      */
35190     ensureVisible : function(callback){
35191         var tree = this.getOwnerTree();
35192         tree.expandPath(this.parentNode.getPath(), false, function(){
35193             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35194             Roo.callback(callback);
35195         }.createDelegate(this));
35196     },
35197
35198     /**
35199      * Expand all child nodes
35200      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35201      */
35202     expandChildNodes : function(deep){
35203         var cs = this.childNodes;
35204         for(var i = 0, len = cs.length; i < len; i++) {
35205                 cs[i].expand(deep);
35206         }
35207     },
35208
35209     /**
35210      * Collapse all child nodes
35211      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35212      */
35213     collapseChildNodes : function(deep){
35214         var cs = this.childNodes;
35215         for(var i = 0, len = cs.length; i < len; i++) {
35216                 cs[i].collapse(deep);
35217         }
35218     },
35219
35220     /**
35221      * Disables this node
35222      */
35223     disable : function(){
35224         this.disabled = true;
35225         this.unselect();
35226         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35227             this.ui.onDisableChange(this, true);
35228         }
35229         this.fireEvent("disabledchange", this, true);
35230     },
35231
35232     /**
35233      * Enables this node
35234      */
35235     enable : function(){
35236         this.disabled = false;
35237         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35238             this.ui.onDisableChange(this, false);
35239         }
35240         this.fireEvent("disabledchange", this, false);
35241     },
35242
35243     // private
35244     renderChildren : function(suppressEvent){
35245         if(suppressEvent !== false){
35246             this.fireEvent("beforechildrenrendered", this);
35247         }
35248         var cs = this.childNodes;
35249         for(var i = 0, len = cs.length; i < len; i++){
35250             cs[i].render(true);
35251         }
35252         this.childrenRendered = true;
35253     },
35254
35255     // private
35256     sort : function(fn, scope){
35257         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35258         if(this.childrenRendered){
35259             var cs = this.childNodes;
35260             for(var i = 0, len = cs.length; i < len; i++){
35261                 cs[i].render(true);
35262             }
35263         }
35264     },
35265
35266     // private
35267     render : function(bulkRender){
35268         this.ui.render(bulkRender);
35269         if(!this.rendered){
35270             this.rendered = true;
35271             if(this.expanded){
35272                 this.expanded = false;
35273                 this.expand(false, false);
35274             }
35275         }
35276     },
35277
35278     // private
35279     renderIndent : function(deep, refresh){
35280         if(refresh){
35281             this.ui.childIndent = null;
35282         }
35283         this.ui.renderIndent();
35284         if(deep === true && this.childrenRendered){
35285             var cs = this.childNodes;
35286             for(var i = 0, len = cs.length; i < len; i++){
35287                 cs[i].renderIndent(true, refresh);
35288             }
35289         }
35290     }
35291 });/*
35292  * Based on:
35293  * Ext JS Library 1.1.1
35294  * Copyright(c) 2006-2007, Ext JS, LLC.
35295  *
35296  * Originally Released Under LGPL - original licence link has changed is not relivant.
35297  *
35298  * Fork - LGPL
35299  * <script type="text/javascript">
35300  */
35301  
35302 /**
35303  * @class Roo.tree.AsyncTreeNode
35304  * @extends Roo.tree.TreeNode
35305  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35306  * @constructor
35307  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35308  */
35309  Roo.tree.AsyncTreeNode = function(config){
35310     this.loaded = false;
35311     this.loading = false;
35312     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35313     /**
35314     * @event beforeload
35315     * Fires before this node is loaded, return false to cancel
35316     * @param {Node} this This node
35317     */
35318     this.addEvents({'beforeload':true, 'load': true});
35319     /**
35320     * @event load
35321     * Fires when this node is loaded
35322     * @param {Node} this This node
35323     */
35324     /**
35325      * The loader used by this node (defaults to using the tree's defined loader)
35326      * @type TreeLoader
35327      * @property loader
35328      */
35329 };
35330 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35331     expand : function(deep, anim, callback){
35332         if(this.loading){ // if an async load is already running, waiting til it's done
35333             var timer;
35334             var f = function(){
35335                 if(!this.loading){ // done loading
35336                     clearInterval(timer);
35337                     this.expand(deep, anim, callback);
35338                 }
35339             }.createDelegate(this);
35340             timer = setInterval(f, 200);
35341             return;
35342         }
35343         if(!this.loaded){
35344             if(this.fireEvent("beforeload", this) === false){
35345                 return;
35346             }
35347             this.loading = true;
35348             this.ui.beforeLoad(this);
35349             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35350             if(loader){
35351                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35352                 return;
35353             }
35354         }
35355         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35356     },
35357     
35358     /**
35359      * Returns true if this node is currently loading
35360      * @return {Boolean}
35361      */
35362     isLoading : function(){
35363         return this.loading;  
35364     },
35365     
35366     loadComplete : function(deep, anim, callback){
35367         this.loading = false;
35368         this.loaded = true;
35369         this.ui.afterLoad(this);
35370         this.fireEvent("load", this);
35371         this.expand(deep, anim, callback);
35372     },
35373     
35374     /**
35375      * Returns true if this node has been loaded
35376      * @return {Boolean}
35377      */
35378     isLoaded : function(){
35379         return this.loaded;
35380     },
35381     
35382     hasChildNodes : function(){
35383         if(!this.isLeaf() && !this.loaded){
35384             return true;
35385         }else{
35386             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35387         }
35388     },
35389
35390     /**
35391      * Trigger a reload for this node
35392      * @param {Function} callback
35393      */
35394     reload : function(callback){
35395         this.collapse(false, false);
35396         while(this.firstChild){
35397             this.removeChild(this.firstChild);
35398         }
35399         this.childrenRendered = false;
35400         this.loaded = false;
35401         if(this.isHiddenRoot()){
35402             this.expanded = false;
35403         }
35404         this.expand(false, false, callback);
35405     }
35406 });/*
35407  * Based on:
35408  * Ext JS Library 1.1.1
35409  * Copyright(c) 2006-2007, Ext JS, LLC.
35410  *
35411  * Originally Released Under LGPL - original licence link has changed is not relivant.
35412  *
35413  * Fork - LGPL
35414  * <script type="text/javascript">
35415  */
35416  
35417 /**
35418  * @class Roo.tree.TreeNodeUI
35419  * @constructor
35420  * @param {Object} node The node to render
35421  * The TreeNode UI implementation is separate from the
35422  * tree implementation. Unless you are customizing the tree UI,
35423  * you should never have to use this directly.
35424  */
35425 Roo.tree.TreeNodeUI = function(node){
35426     this.node = node;
35427     this.rendered = false;
35428     this.animating = false;
35429     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35430 };
35431
35432 Roo.tree.TreeNodeUI.prototype = {
35433     removeChild : function(node){
35434         if(this.rendered){
35435             this.ctNode.removeChild(node.ui.getEl());
35436         }
35437     },
35438
35439     beforeLoad : function(){
35440          this.addClass("x-tree-node-loading");
35441     },
35442
35443     afterLoad : function(){
35444          this.removeClass("x-tree-node-loading");
35445     },
35446
35447     onTextChange : function(node, text, oldText){
35448         if(this.rendered){
35449             this.textNode.innerHTML = text;
35450         }
35451     },
35452
35453     onDisableChange : function(node, state){
35454         this.disabled = state;
35455         if(state){
35456             this.addClass("x-tree-node-disabled");
35457         }else{
35458             this.removeClass("x-tree-node-disabled");
35459         }
35460     },
35461
35462     onSelectedChange : function(state){
35463         if(state){
35464             this.focus();
35465             this.addClass("x-tree-selected");
35466         }else{
35467             //this.blur();
35468             this.removeClass("x-tree-selected");
35469         }
35470     },
35471
35472     onMove : function(tree, node, oldParent, newParent, index, refNode){
35473         this.childIndent = null;
35474         if(this.rendered){
35475             var targetNode = newParent.ui.getContainer();
35476             if(!targetNode){//target not rendered
35477                 this.holder = document.createElement("div");
35478                 this.holder.appendChild(this.wrap);
35479                 return;
35480             }
35481             var insertBefore = refNode ? refNode.ui.getEl() : null;
35482             if(insertBefore){
35483                 targetNode.insertBefore(this.wrap, insertBefore);
35484             }else{
35485                 targetNode.appendChild(this.wrap);
35486             }
35487             this.node.renderIndent(true);
35488         }
35489     },
35490
35491     addClass : function(cls){
35492         if(this.elNode){
35493             Roo.fly(this.elNode).addClass(cls);
35494         }
35495     },
35496
35497     removeClass : function(cls){
35498         if(this.elNode){
35499             Roo.fly(this.elNode).removeClass(cls);
35500         }
35501     },
35502
35503     remove : function(){
35504         if(this.rendered){
35505             this.holder = document.createElement("div");
35506             this.holder.appendChild(this.wrap);
35507         }
35508     },
35509
35510     fireEvent : function(){
35511         return this.node.fireEvent.apply(this.node, arguments);
35512     },
35513
35514     initEvents : function(){
35515         this.node.on("move", this.onMove, this);
35516         var E = Roo.EventManager;
35517         var a = this.anchor;
35518
35519         var el = Roo.fly(a, '_treeui');
35520
35521         if(Roo.isOpera){ // opera render bug ignores the CSS
35522             el.setStyle("text-decoration", "none");
35523         }
35524
35525         el.on("click", this.onClick, this);
35526         el.on("dblclick", this.onDblClick, this);
35527
35528         if(this.checkbox){
35529             Roo.EventManager.on(this.checkbox,
35530                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35531         }
35532
35533         el.on("contextmenu", this.onContextMenu, this);
35534
35535         var icon = Roo.fly(this.iconNode);
35536         icon.on("click", this.onClick, this);
35537         icon.on("dblclick", this.onDblClick, this);
35538         icon.on("contextmenu", this.onContextMenu, this);
35539         E.on(this.ecNode, "click", this.ecClick, this, true);
35540
35541         if(this.node.disabled){
35542             this.addClass("x-tree-node-disabled");
35543         }
35544         if(this.node.hidden){
35545             this.addClass("x-tree-node-disabled");
35546         }
35547         var ot = this.node.getOwnerTree();
35548         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
35549         if(dd && (!this.node.isRoot || ot.rootVisible)){
35550             Roo.dd.Registry.register(this.elNode, {
35551                 node: this.node,
35552                 handles: this.getDDHandles(),
35553                 isHandle: false
35554             });
35555         }
35556     },
35557
35558     getDDHandles : function(){
35559         return [this.iconNode, this.textNode];
35560     },
35561
35562     hide : function(){
35563         if(this.rendered){
35564             this.wrap.style.display = "none";
35565         }
35566     },
35567
35568     show : function(){
35569         if(this.rendered){
35570             this.wrap.style.display = "";
35571         }
35572     },
35573
35574     onContextMenu : function(e){
35575         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35576             e.preventDefault();
35577             this.focus();
35578             this.fireEvent("contextmenu", this.node, e);
35579         }
35580     },
35581
35582     onClick : function(e){
35583         if(this.dropping){
35584             e.stopEvent();
35585             return;
35586         }
35587         if(this.fireEvent("beforeclick", this.node, e) !== false){
35588             if(!this.disabled && this.node.attributes.href){
35589                 this.fireEvent("click", this.node, e);
35590                 return;
35591             }
35592             e.preventDefault();
35593             if(this.disabled){
35594                 return;
35595             }
35596
35597             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35598                 this.node.toggle();
35599             }
35600
35601             this.fireEvent("click", this.node, e);
35602         }else{
35603             e.stopEvent();
35604         }
35605     },
35606
35607     onDblClick : function(e){
35608         e.preventDefault();
35609         if(this.disabled){
35610             return;
35611         }
35612         if(this.checkbox){
35613             this.toggleCheck();
35614         }
35615         if(!this.animating && this.node.hasChildNodes()){
35616             this.node.toggle();
35617         }
35618         this.fireEvent("dblclick", this.node, e);
35619     },
35620
35621     onCheckChange : function(){
35622         var checked = this.checkbox.checked;
35623         this.node.attributes.checked = checked;
35624         this.fireEvent('checkchange', this.node, checked);
35625     },
35626
35627     ecClick : function(e){
35628         if(!this.animating && this.node.hasChildNodes()){
35629             this.node.toggle();
35630         }
35631     },
35632
35633     startDrop : function(){
35634         this.dropping = true;
35635     },
35636
35637     // delayed drop so the click event doesn't get fired on a drop
35638     endDrop : function(){
35639        setTimeout(function(){
35640            this.dropping = false;
35641        }.createDelegate(this), 50);
35642     },
35643
35644     expand : function(){
35645         this.updateExpandIcon();
35646         this.ctNode.style.display = "";
35647     },
35648
35649     focus : function(){
35650         if(!this.node.preventHScroll){
35651             try{this.anchor.focus();
35652             }catch(e){}
35653         }else if(!Roo.isIE){
35654             try{
35655                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35656                 var l = noscroll.scrollLeft;
35657                 this.anchor.focus();
35658                 noscroll.scrollLeft = l;
35659             }catch(e){}
35660         }
35661     },
35662
35663     toggleCheck : function(value){
35664         var cb = this.checkbox;
35665         if(cb){
35666             cb.checked = (value === undefined ? !cb.checked : value);
35667         }
35668     },
35669
35670     blur : function(){
35671         try{
35672             this.anchor.blur();
35673         }catch(e){}
35674     },
35675
35676     animExpand : function(callback){
35677         var ct = Roo.get(this.ctNode);
35678         ct.stopFx();
35679         if(!this.node.hasChildNodes()){
35680             this.updateExpandIcon();
35681             this.ctNode.style.display = "";
35682             Roo.callback(callback);
35683             return;
35684         }
35685         this.animating = true;
35686         this.updateExpandIcon();
35687
35688         ct.slideIn('t', {
35689            callback : function(){
35690                this.animating = false;
35691                Roo.callback(callback);
35692             },
35693             scope: this,
35694             duration: this.node.ownerTree.duration || .25
35695         });
35696     },
35697
35698     highlight : function(){
35699         var tree = this.node.getOwnerTree();
35700         Roo.fly(this.wrap).highlight(
35701             tree.hlColor || "C3DAF9",
35702             {endColor: tree.hlBaseColor}
35703         );
35704     },
35705
35706     collapse : function(){
35707         this.updateExpandIcon();
35708         this.ctNode.style.display = "none";
35709     },
35710
35711     animCollapse : function(callback){
35712         var ct = Roo.get(this.ctNode);
35713         ct.enableDisplayMode('block');
35714         ct.stopFx();
35715
35716         this.animating = true;
35717         this.updateExpandIcon();
35718
35719         ct.slideOut('t', {
35720             callback : function(){
35721                this.animating = false;
35722                Roo.callback(callback);
35723             },
35724             scope: this,
35725             duration: this.node.ownerTree.duration || .25
35726         });
35727     },
35728
35729     getContainer : function(){
35730         return this.ctNode;
35731     },
35732
35733     getEl : function(){
35734         return this.wrap;
35735     },
35736
35737     appendDDGhost : function(ghostNode){
35738         ghostNode.appendChild(this.elNode.cloneNode(true));
35739     },
35740
35741     getDDRepairXY : function(){
35742         return Roo.lib.Dom.getXY(this.iconNode);
35743     },
35744
35745     onRender : function(){
35746         this.render();
35747     },
35748
35749     render : function(bulkRender){
35750         var n = this.node, a = n.attributes;
35751         var targetNode = n.parentNode ?
35752               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35753
35754         if(!this.rendered){
35755             this.rendered = true;
35756
35757             this.renderElements(n, a, targetNode, bulkRender);
35758
35759             if(a.qtip){
35760                if(this.textNode.setAttributeNS){
35761                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35762                    if(a.qtipTitle){
35763                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35764                    }
35765                }else{
35766                    this.textNode.setAttribute("ext:qtip", a.qtip);
35767                    if(a.qtipTitle){
35768                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35769                    }
35770                }
35771             }else if(a.qtipCfg){
35772                 a.qtipCfg.target = Roo.id(this.textNode);
35773                 Roo.QuickTips.register(a.qtipCfg);
35774             }
35775             this.initEvents();
35776             if(!this.node.expanded){
35777                 this.updateExpandIcon();
35778             }
35779         }else{
35780             if(bulkRender === true) {
35781                 targetNode.appendChild(this.wrap);
35782             }
35783         }
35784     },
35785
35786     renderElements : function(n, a, targetNode, bulkRender)
35787     {
35788         // add some indent caching, this helps performance when rendering a large tree
35789         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35790         var t = n.getOwnerTree();
35791         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35792         if (typeof(n.attributes.html) != 'undefined') {
35793             txt = n.attributes.html;
35794         }
35795         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
35796         var cb = typeof a.checked == 'boolean';
35797         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35798         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35799             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35800             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35801             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35802             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35803             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35804              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35805                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35806             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35807             "</li>"];
35808
35809         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35810             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35811                                 n.nextSibling.ui.getEl(), buf.join(""));
35812         }else{
35813             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35814         }
35815
35816         this.elNode = this.wrap.childNodes[0];
35817         this.ctNode = this.wrap.childNodes[1];
35818         var cs = this.elNode.childNodes;
35819         this.indentNode = cs[0];
35820         this.ecNode = cs[1];
35821         this.iconNode = cs[2];
35822         var index = 3;
35823         if(cb){
35824             this.checkbox = cs[3];
35825             index++;
35826         }
35827         this.anchor = cs[index];
35828         this.textNode = cs[index].firstChild;
35829     },
35830
35831     getAnchor : function(){
35832         return this.anchor;
35833     },
35834
35835     getTextEl : function(){
35836         return this.textNode;
35837     },
35838
35839     getIconEl : function(){
35840         return this.iconNode;
35841     },
35842
35843     isChecked : function(){
35844         return this.checkbox ? this.checkbox.checked : false;
35845     },
35846
35847     updateExpandIcon : function(){
35848         if(this.rendered){
35849             var n = this.node, c1, c2;
35850             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
35851             var hasChild = n.hasChildNodes();
35852             if(hasChild){
35853                 if(n.expanded){
35854                     cls += "-minus";
35855                     c1 = "x-tree-node-collapsed";
35856                     c2 = "x-tree-node-expanded";
35857                 }else{
35858                     cls += "-plus";
35859                     c1 = "x-tree-node-expanded";
35860                     c2 = "x-tree-node-collapsed";
35861                 }
35862                 if(this.wasLeaf){
35863                     this.removeClass("x-tree-node-leaf");
35864                     this.wasLeaf = false;
35865                 }
35866                 if(this.c1 != c1 || this.c2 != c2){
35867                     Roo.fly(this.elNode).replaceClass(c1, c2);
35868                     this.c1 = c1; this.c2 = c2;
35869                 }
35870             }else{
35871                 // this changes non-leafs into leafs if they have no children.
35872                 // it's not very rational behaviour..
35873                 
35874                 if(!this.wasLeaf && this.node.leaf){
35875                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
35876                     delete this.c1;
35877                     delete this.c2;
35878                     this.wasLeaf = true;
35879                 }
35880             }
35881             var ecc = "x-tree-ec-icon "+cls;
35882             if(this.ecc != ecc){
35883                 this.ecNode.className = ecc;
35884                 this.ecc = ecc;
35885             }
35886         }
35887     },
35888
35889     getChildIndent : function(){
35890         if(!this.childIndent){
35891             var buf = [];
35892             var p = this.node;
35893             while(p){
35894                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
35895                     if(!p.isLast()) {
35896                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
35897                     } else {
35898                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
35899                     }
35900                 }
35901                 p = p.parentNode;
35902             }
35903             this.childIndent = buf.join("");
35904         }
35905         return this.childIndent;
35906     },
35907
35908     renderIndent : function(){
35909         if(this.rendered){
35910             var indent = "";
35911             var p = this.node.parentNode;
35912             if(p){
35913                 indent = p.ui.getChildIndent();
35914             }
35915             if(this.indentMarkup != indent){ // don't rerender if not required
35916                 this.indentNode.innerHTML = indent;
35917                 this.indentMarkup = indent;
35918             }
35919             this.updateExpandIcon();
35920         }
35921     }
35922 };
35923
35924 Roo.tree.RootTreeNodeUI = function(){
35925     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
35926 };
35927 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
35928     render : function(){
35929         if(!this.rendered){
35930             var targetNode = this.node.ownerTree.innerCt.dom;
35931             this.node.expanded = true;
35932             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
35933             this.wrap = this.ctNode = targetNode.firstChild;
35934         }
35935     },
35936     collapse : function(){
35937     },
35938     expand : function(){
35939     }
35940 });/*
35941  * Based on:
35942  * Ext JS Library 1.1.1
35943  * Copyright(c) 2006-2007, Ext JS, LLC.
35944  *
35945  * Originally Released Under LGPL - original licence link has changed is not relivant.
35946  *
35947  * Fork - LGPL
35948  * <script type="text/javascript">
35949  */
35950 /**
35951  * @class Roo.tree.TreeLoader
35952  * @extends Roo.util.Observable
35953  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
35954  * nodes from a specified URL. The response must be a javascript Array definition
35955  * who's elements are node definition objects. eg:
35956  * <pre><code>
35957 {  success : true,
35958    data :      [
35959    
35960     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
35961     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
35962     ]
35963 }
35964
35965
35966 </code></pre>
35967  * <br><br>
35968  * The old style respose with just an array is still supported, but not recommended.
35969  * <br><br>
35970  *
35971  * A server request is sent, and child nodes are loaded only when a node is expanded.
35972  * The loading node's id is passed to the server under the parameter name "node" to
35973  * enable the server to produce the correct child nodes.
35974  * <br><br>
35975  * To pass extra parameters, an event handler may be attached to the "beforeload"
35976  * event, and the parameters specified in the TreeLoader's baseParams property:
35977  * <pre><code>
35978     myTreeLoader.on("beforeload", function(treeLoader, node) {
35979         this.baseParams.category = node.attributes.category;
35980     }, this);
35981 </code></pre><
35982  * This would pass an HTTP parameter called "category" to the server containing
35983  * the value of the Node's "category" attribute.
35984  * @constructor
35985  * Creates a new Treeloader.
35986  * @param {Object} config A config object containing config properties.
35987  */
35988 Roo.tree.TreeLoader = function(config){
35989     this.baseParams = {};
35990     this.requestMethod = "POST";
35991     Roo.apply(this, config);
35992
35993     this.addEvents({
35994     
35995         /**
35996          * @event beforeload
35997          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
35998          * @param {Object} This TreeLoader object.
35999          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36000          * @param {Object} callback The callback function specified in the {@link #load} call.
36001          */
36002         beforeload : true,
36003         /**
36004          * @event load
36005          * Fires when the node has been successfuly loaded.
36006          * @param {Object} This TreeLoader object.
36007          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36008          * @param {Object} response The response object containing the data from the server.
36009          */
36010         load : true,
36011         /**
36012          * @event loadexception
36013          * Fires if the network request failed.
36014          * @param {Object} This TreeLoader object.
36015          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36016          * @param {Object} response The response object containing the data from the server.
36017          */
36018         loadexception : true,
36019         /**
36020          * @event create
36021          * Fires before a node is created, enabling you to return custom Node types 
36022          * @param {Object} This TreeLoader object.
36023          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
36024          */
36025         create : true
36026     });
36027
36028     Roo.tree.TreeLoader.superclass.constructor.call(this);
36029 };
36030
36031 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
36032     /**
36033     * @cfg {String} dataUrl The URL from which to request a Json string which
36034     * specifies an array of node definition object representing the child nodes
36035     * to be loaded.
36036     */
36037     /**
36038     * @cfg {String} requestMethod either GET or POST
36039     * defaults to POST (due to BC)
36040     * to be loaded.
36041     */
36042     /**
36043     * @cfg {Object} baseParams (optional) An object containing properties which
36044     * specify HTTP parameters to be passed to each request for child nodes.
36045     */
36046     /**
36047     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
36048     * created by this loader. If the attributes sent by the server have an attribute in this object,
36049     * they take priority.
36050     */
36051     /**
36052     * @cfg {Object} uiProviders (optional) An object containing properties which
36053     * 
36054     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
36055     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
36056     * <i>uiProvider</i> attribute of a returned child node is a string rather
36057     * than a reference to a TreeNodeUI implementation, this that string value
36058     * is used as a property name in the uiProviders object. You can define the provider named
36059     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
36060     */
36061     uiProviders : {},
36062
36063     /**
36064     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
36065     * child nodes before loading.
36066     */
36067     clearOnLoad : true,
36068
36069     /**
36070     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
36071     * property on loading, rather than expecting an array. (eg. more compatible to a standard
36072     * Grid query { data : [ .....] }
36073     */
36074     
36075     root : false,
36076      /**
36077     * @cfg {String} queryParam (optional) 
36078     * Name of the query as it will be passed on the querystring (defaults to 'node')
36079     * eg. the request will be ?node=[id]
36080     */
36081     
36082     
36083     queryParam: false,
36084     
36085     /**
36086      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36087      * This is called automatically when a node is expanded, but may be used to reload
36088      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36089      * @param {Roo.tree.TreeNode} node
36090      * @param {Function} callback
36091      */
36092     load : function(node, callback){
36093         if(this.clearOnLoad){
36094             while(node.firstChild){
36095                 node.removeChild(node.firstChild);
36096             }
36097         }
36098         if(node.attributes.children){ // preloaded json children
36099             var cs = node.attributes.children;
36100             for(var i = 0, len = cs.length; i < len; i++){
36101                 node.appendChild(this.createNode(cs[i]));
36102             }
36103             if(typeof callback == "function"){
36104                 callback();
36105             }
36106         }else if(this.dataUrl){
36107             this.requestData(node, callback);
36108         }
36109     },
36110
36111     getParams: function(node){
36112         var buf = [], bp = this.baseParams;
36113         for(var key in bp){
36114             if(typeof bp[key] != "function"){
36115                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36116             }
36117         }
36118         var n = this.queryParam === false ? 'node' : this.queryParam;
36119         buf.push(n + "=", encodeURIComponent(node.id));
36120         return buf.join("");
36121     },
36122
36123     requestData : function(node, callback){
36124         if(this.fireEvent("beforeload", this, node, callback) !== false){
36125             this.transId = Roo.Ajax.request({
36126                 method:this.requestMethod,
36127                 url: this.dataUrl||this.url,
36128                 success: this.handleResponse,
36129                 failure: this.handleFailure,
36130                 scope: this,
36131                 argument: {callback: callback, node: node},
36132                 params: this.getParams(node)
36133             });
36134         }else{
36135             // if the load is cancelled, make sure we notify
36136             // the node that we are done
36137             if(typeof callback == "function"){
36138                 callback();
36139             }
36140         }
36141     },
36142
36143     isLoading : function(){
36144         return this.transId ? true : false;
36145     },
36146
36147     abort : function(){
36148         if(this.isLoading()){
36149             Roo.Ajax.abort(this.transId);
36150         }
36151     },
36152
36153     // private
36154     createNode : function(attr)
36155     {
36156         // apply baseAttrs, nice idea Corey!
36157         if(this.baseAttrs){
36158             Roo.applyIf(attr, this.baseAttrs);
36159         }
36160         if(this.applyLoader !== false){
36161             attr.loader = this;
36162         }
36163         // uiProvider = depreciated..
36164         
36165         if(typeof(attr.uiProvider) == 'string'){
36166            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36167                 /**  eval:var:attr */ eval(attr.uiProvider);
36168         }
36169         if(typeof(this.uiProviders['default']) != 'undefined') {
36170             attr.uiProvider = this.uiProviders['default'];
36171         }
36172         
36173         this.fireEvent('create', this, attr);
36174         
36175         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36176         return(attr.leaf ?
36177                         new Roo.tree.TreeNode(attr) :
36178                         new Roo.tree.AsyncTreeNode(attr));
36179     },
36180
36181     processResponse : function(response, node, callback)
36182     {
36183         var json = response.responseText;
36184         try {
36185             
36186             var o = Roo.decode(json);
36187             
36188             if (this.root === false && typeof(o.success) != undefined) {
36189                 this.root = 'data'; // the default behaviour for list like data..
36190                 }
36191                 
36192             if (this.root !== false &&  !o.success) {
36193                 // it's a failure condition.
36194                 var a = response.argument;
36195                 this.fireEvent("loadexception", this, a.node, response);
36196                 Roo.log("Load failed - should have a handler really");
36197                 return;
36198             }
36199             
36200             
36201             
36202             if (this.root !== false) {
36203                  o = o[this.root];
36204             }
36205             
36206             for(var i = 0, len = o.length; i < len; i++){
36207                 var n = this.createNode(o[i]);
36208                 if(n){
36209                     node.appendChild(n);
36210                 }
36211             }
36212             if(typeof callback == "function"){
36213                 callback(this, node);
36214             }
36215         }catch(e){
36216             this.handleFailure(response);
36217         }
36218     },
36219
36220     handleResponse : function(response){
36221         this.transId = false;
36222         var a = response.argument;
36223         this.processResponse(response, a.node, a.callback);
36224         this.fireEvent("load", this, a.node, response);
36225     },
36226
36227     handleFailure : function(response)
36228     {
36229         // should handle failure better..
36230         this.transId = false;
36231         var a = response.argument;
36232         this.fireEvent("loadexception", this, a.node, response);
36233         if(typeof a.callback == "function"){
36234             a.callback(this, a.node);
36235         }
36236     }
36237 });/*
36238  * Based on:
36239  * Ext JS Library 1.1.1
36240  * Copyright(c) 2006-2007, Ext JS, LLC.
36241  *
36242  * Originally Released Under LGPL - original licence link has changed is not relivant.
36243  *
36244  * Fork - LGPL
36245  * <script type="text/javascript">
36246  */
36247
36248 /**
36249 * @class Roo.tree.TreeFilter
36250 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36251 * @param {TreePanel} tree
36252 * @param {Object} config (optional)
36253  */
36254 Roo.tree.TreeFilter = function(tree, config){
36255     this.tree = tree;
36256     this.filtered = {};
36257     Roo.apply(this, config);
36258 };
36259
36260 Roo.tree.TreeFilter.prototype = {
36261     clearBlank:false,
36262     reverse:false,
36263     autoClear:false,
36264     remove:false,
36265
36266      /**
36267      * Filter the data by a specific attribute.
36268      * @param {String/RegExp} value Either string that the attribute value
36269      * should start with or a RegExp to test against the attribute
36270      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36271      * @param {TreeNode} startNode (optional) The node to start the filter at.
36272      */
36273     filter : function(value, attr, startNode){
36274         attr = attr || "text";
36275         var f;
36276         if(typeof value == "string"){
36277             var vlen = value.length;
36278             // auto clear empty filter
36279             if(vlen == 0 && this.clearBlank){
36280                 this.clear();
36281                 return;
36282             }
36283             value = value.toLowerCase();
36284             f = function(n){
36285                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36286             };
36287         }else if(value.exec){ // regex?
36288             f = function(n){
36289                 return value.test(n.attributes[attr]);
36290             };
36291         }else{
36292             throw 'Illegal filter type, must be string or regex';
36293         }
36294         this.filterBy(f, null, startNode);
36295         },
36296
36297     /**
36298      * Filter by a function. The passed function will be called with each
36299      * node in the tree (or from the startNode). If the function returns true, the node is kept
36300      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36301      * @param {Function} fn The filter function
36302      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36303      */
36304     filterBy : function(fn, scope, startNode){
36305         startNode = startNode || this.tree.root;
36306         if(this.autoClear){
36307             this.clear();
36308         }
36309         var af = this.filtered, rv = this.reverse;
36310         var f = function(n){
36311             if(n == startNode){
36312                 return true;
36313             }
36314             if(af[n.id]){
36315                 return false;
36316             }
36317             var m = fn.call(scope || n, n);
36318             if(!m || rv){
36319                 af[n.id] = n;
36320                 n.ui.hide();
36321                 return false;
36322             }
36323             return true;
36324         };
36325         startNode.cascade(f);
36326         if(this.remove){
36327            for(var id in af){
36328                if(typeof id != "function"){
36329                    var n = af[id];
36330                    if(n && n.parentNode){
36331                        n.parentNode.removeChild(n);
36332                    }
36333                }
36334            }
36335         }
36336     },
36337
36338     /**
36339      * Clears the current filter. Note: with the "remove" option
36340      * set a filter cannot be cleared.
36341      */
36342     clear : function(){
36343         var t = this.tree;
36344         var af = this.filtered;
36345         for(var id in af){
36346             if(typeof id != "function"){
36347                 var n = af[id];
36348                 if(n){
36349                     n.ui.show();
36350                 }
36351             }
36352         }
36353         this.filtered = {};
36354     }
36355 };
36356 /*
36357  * Based on:
36358  * Ext JS Library 1.1.1
36359  * Copyright(c) 2006-2007, Ext JS, LLC.
36360  *
36361  * Originally Released Under LGPL - original licence link has changed is not relivant.
36362  *
36363  * Fork - LGPL
36364  * <script type="text/javascript">
36365  */
36366  
36367
36368 /**
36369  * @class Roo.tree.TreeSorter
36370  * Provides sorting of nodes in a TreePanel
36371  * 
36372  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36373  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36374  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36375  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36376  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36377  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36378  * @constructor
36379  * @param {TreePanel} tree
36380  * @param {Object} config
36381  */
36382 Roo.tree.TreeSorter = function(tree, config){
36383     Roo.apply(this, config);
36384     tree.on("beforechildrenrendered", this.doSort, this);
36385     tree.on("append", this.updateSort, this);
36386     tree.on("insert", this.updateSort, this);
36387     
36388     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36389     var p = this.property || "text";
36390     var sortType = this.sortType;
36391     var fs = this.folderSort;
36392     var cs = this.caseSensitive === true;
36393     var leafAttr = this.leafAttr || 'leaf';
36394
36395     this.sortFn = function(n1, n2){
36396         if(fs){
36397             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36398                 return 1;
36399             }
36400             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36401                 return -1;
36402             }
36403         }
36404         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36405         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36406         if(v1 < v2){
36407                         return dsc ? +1 : -1;
36408                 }else if(v1 > v2){
36409                         return dsc ? -1 : +1;
36410         }else{
36411                 return 0;
36412         }
36413     };
36414 };
36415
36416 Roo.tree.TreeSorter.prototype = {
36417     doSort : function(node){
36418         node.sort(this.sortFn);
36419     },
36420     
36421     compareNodes : function(n1, n2){
36422         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36423     },
36424     
36425     updateSort : function(tree, node){
36426         if(node.childrenRendered){
36427             this.doSort.defer(1, this, [node]);
36428         }
36429     }
36430 };/*
36431  * Based on:
36432  * Ext JS Library 1.1.1
36433  * Copyright(c) 2006-2007, Ext JS, LLC.
36434  *
36435  * Originally Released Under LGPL - original licence link has changed is not relivant.
36436  *
36437  * Fork - LGPL
36438  * <script type="text/javascript">
36439  */
36440
36441 if(Roo.dd.DropZone){
36442     
36443 Roo.tree.TreeDropZone = function(tree, config){
36444     this.allowParentInsert = false;
36445     this.allowContainerDrop = false;
36446     this.appendOnly = false;
36447     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36448     this.tree = tree;
36449     this.lastInsertClass = "x-tree-no-status";
36450     this.dragOverData = {};
36451 };
36452
36453 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36454     ddGroup : "TreeDD",
36455     scroll:  true,
36456     
36457     expandDelay : 1000,
36458     
36459     expandNode : function(node){
36460         if(node.hasChildNodes() && !node.isExpanded()){
36461             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36462         }
36463     },
36464     
36465     queueExpand : function(node){
36466         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36467     },
36468     
36469     cancelExpand : function(){
36470         if(this.expandProcId){
36471             clearTimeout(this.expandProcId);
36472             this.expandProcId = false;
36473         }
36474     },
36475     
36476     isValidDropPoint : function(n, pt, dd, e, data){
36477         if(!n || !data){ return false; }
36478         var targetNode = n.node;
36479         var dropNode = data.node;
36480         // default drop rules
36481         if(!(targetNode && targetNode.isTarget && pt)){
36482             return false;
36483         }
36484         if(pt == "append" && targetNode.allowChildren === false){
36485             return false;
36486         }
36487         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36488             return false;
36489         }
36490         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36491             return false;
36492         }
36493         // reuse the object
36494         var overEvent = this.dragOverData;
36495         overEvent.tree = this.tree;
36496         overEvent.target = targetNode;
36497         overEvent.data = data;
36498         overEvent.point = pt;
36499         overEvent.source = dd;
36500         overEvent.rawEvent = e;
36501         overEvent.dropNode = dropNode;
36502         overEvent.cancel = false;  
36503         var result = this.tree.fireEvent("nodedragover", overEvent);
36504         return overEvent.cancel === false && result !== false;
36505     },
36506     
36507     getDropPoint : function(e, n, dd)
36508     {
36509         var tn = n.node;
36510         if(tn.isRoot){
36511             return tn.allowChildren !== false ? "append" : false; // always append for root
36512         }
36513         var dragEl = n.ddel;
36514         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36515         var y = Roo.lib.Event.getPageY(e);
36516         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36517         
36518         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36519         var noAppend = tn.allowChildren === false;
36520         if(this.appendOnly || tn.parentNode.allowChildren === false){
36521             return noAppend ? false : "append";
36522         }
36523         var noBelow = false;
36524         if(!this.allowParentInsert){
36525             noBelow = tn.hasChildNodes() && tn.isExpanded();
36526         }
36527         var q = (b - t) / (noAppend ? 2 : 3);
36528         if(y >= t && y < (t + q)){
36529             return "above";
36530         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36531             return "below";
36532         }else{
36533             return "append";
36534         }
36535     },
36536     
36537     onNodeEnter : function(n, dd, e, data)
36538     {
36539         this.cancelExpand();
36540     },
36541     
36542     onNodeOver : function(n, dd, e, data)
36543     {
36544        
36545         var pt = this.getDropPoint(e, n, dd);
36546         var node = n.node;
36547         
36548         // auto node expand check
36549         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36550             this.queueExpand(node);
36551         }else if(pt != "append"){
36552             this.cancelExpand();
36553         }
36554         
36555         // set the insert point style on the target node
36556         var returnCls = this.dropNotAllowed;
36557         if(this.isValidDropPoint(n, pt, dd, e, data)){
36558            if(pt){
36559                var el = n.ddel;
36560                var cls;
36561                if(pt == "above"){
36562                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36563                    cls = "x-tree-drag-insert-above";
36564                }else if(pt == "below"){
36565                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36566                    cls = "x-tree-drag-insert-below";
36567                }else{
36568                    returnCls = "x-tree-drop-ok-append";
36569                    cls = "x-tree-drag-append";
36570                }
36571                if(this.lastInsertClass != cls){
36572                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36573                    this.lastInsertClass = cls;
36574                }
36575            }
36576        }
36577        return returnCls;
36578     },
36579     
36580     onNodeOut : function(n, dd, e, data){
36581         
36582         this.cancelExpand();
36583         this.removeDropIndicators(n);
36584     },
36585     
36586     onNodeDrop : function(n, dd, e, data){
36587         var point = this.getDropPoint(e, n, dd);
36588         var targetNode = n.node;
36589         targetNode.ui.startDrop();
36590         if(!this.isValidDropPoint(n, point, dd, e, data)){
36591             targetNode.ui.endDrop();
36592             return false;
36593         }
36594         // first try to find the drop node
36595         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36596         var dropEvent = {
36597             tree : this.tree,
36598             target: targetNode,
36599             data: data,
36600             point: point,
36601             source: dd,
36602             rawEvent: e,
36603             dropNode: dropNode,
36604             cancel: !dropNode   
36605         };
36606         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36607         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36608             targetNode.ui.endDrop();
36609             return false;
36610         }
36611         // allow target changing
36612         targetNode = dropEvent.target;
36613         if(point == "append" && !targetNode.isExpanded()){
36614             targetNode.expand(false, null, function(){
36615                 this.completeDrop(dropEvent);
36616             }.createDelegate(this));
36617         }else{
36618             this.completeDrop(dropEvent);
36619         }
36620         return true;
36621     },
36622     
36623     completeDrop : function(de){
36624         var ns = de.dropNode, p = de.point, t = de.target;
36625         if(!(ns instanceof Array)){
36626             ns = [ns];
36627         }
36628         var n;
36629         for(var i = 0, len = ns.length; i < len; i++){
36630             n = ns[i];
36631             if(p == "above"){
36632                 t.parentNode.insertBefore(n, t);
36633             }else if(p == "below"){
36634                 t.parentNode.insertBefore(n, t.nextSibling);
36635             }else{
36636                 t.appendChild(n);
36637             }
36638         }
36639         n.ui.focus();
36640         if(this.tree.hlDrop){
36641             n.ui.highlight();
36642         }
36643         t.ui.endDrop();
36644         this.tree.fireEvent("nodedrop", de);
36645     },
36646     
36647     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36648         if(this.tree.hlDrop){
36649             dropNode.ui.focus();
36650             dropNode.ui.highlight();
36651         }
36652         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36653     },
36654     
36655     getTree : function(){
36656         return this.tree;
36657     },
36658     
36659     removeDropIndicators : function(n){
36660         if(n && n.ddel){
36661             var el = n.ddel;
36662             Roo.fly(el).removeClass([
36663                     "x-tree-drag-insert-above",
36664                     "x-tree-drag-insert-below",
36665                     "x-tree-drag-append"]);
36666             this.lastInsertClass = "_noclass";
36667         }
36668     },
36669     
36670     beforeDragDrop : function(target, e, id){
36671         this.cancelExpand();
36672         return true;
36673     },
36674     
36675     afterRepair : function(data){
36676         if(data && Roo.enableFx){
36677             data.node.ui.highlight();
36678         }
36679         this.hideProxy();
36680     } 
36681     
36682 });
36683
36684 }
36685 /*
36686  * Based on:
36687  * Ext JS Library 1.1.1
36688  * Copyright(c) 2006-2007, Ext JS, LLC.
36689  *
36690  * Originally Released Under LGPL - original licence link has changed is not relivant.
36691  *
36692  * Fork - LGPL
36693  * <script type="text/javascript">
36694  */
36695  
36696
36697 if(Roo.dd.DragZone){
36698 Roo.tree.TreeDragZone = function(tree, config){
36699     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36700     this.tree = tree;
36701 };
36702
36703 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36704     ddGroup : "TreeDD",
36705    
36706     onBeforeDrag : function(data, e){
36707         var n = data.node;
36708         return n && n.draggable && !n.disabled;
36709     },
36710      
36711     
36712     onInitDrag : function(e){
36713         var data = this.dragData;
36714         this.tree.getSelectionModel().select(data.node);
36715         this.proxy.update("");
36716         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36717         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36718     },
36719     
36720     getRepairXY : function(e, data){
36721         return data.node.ui.getDDRepairXY();
36722     },
36723     
36724     onEndDrag : function(data, e){
36725         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36726         
36727         
36728     },
36729     
36730     onValidDrop : function(dd, e, id){
36731         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36732         this.hideProxy();
36733     },
36734     
36735     beforeInvalidDrop : function(e, id){
36736         // this scrolls the original position back into view
36737         var sm = this.tree.getSelectionModel();
36738         sm.clearSelections();
36739         sm.select(this.dragData.node);
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  * @class Roo.tree.TreeEditor
36754  * @extends Roo.Editor
36755  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36756  * as the editor field.
36757  * @constructor
36758  * @param {Object} config (used to be the tree panel.)
36759  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36760  * 
36761  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36762  * @cfg {Roo.form.TextField|Object} field The field configuration
36763  *
36764  * 
36765  */
36766 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36767     var tree = config;
36768     var field;
36769     if (oldconfig) { // old style..
36770         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36771     } else {
36772         // new style..
36773         tree = config.tree;
36774         config.field = config.field  || {};
36775         config.field.xtype = 'TextField';
36776         field = Roo.factory(config.field, Roo.form);
36777     }
36778     config = config || {};
36779     
36780     
36781     this.addEvents({
36782         /**
36783          * @event beforenodeedit
36784          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36785          * false from the handler of this event.
36786          * @param {Editor} this
36787          * @param {Roo.tree.Node} node 
36788          */
36789         "beforenodeedit" : true
36790     });
36791     
36792     //Roo.log(config);
36793     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36794
36795     this.tree = tree;
36796
36797     tree.on('beforeclick', this.beforeNodeClick, this);
36798     tree.getTreeEl().on('mousedown', this.hide, this);
36799     this.on('complete', this.updateNode, this);
36800     this.on('beforestartedit', this.fitToTree, this);
36801     this.on('startedit', this.bindScroll, this, {delay:10});
36802     this.on('specialkey', this.onSpecialKey, this);
36803 };
36804
36805 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36806     /**
36807      * @cfg {String} alignment
36808      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36809      */
36810     alignment: "l-l",
36811     // inherit
36812     autoSize: false,
36813     /**
36814      * @cfg {Boolean} hideEl
36815      * True to hide the bound element while the editor is displayed (defaults to false)
36816      */
36817     hideEl : false,
36818     /**
36819      * @cfg {String} cls
36820      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
36821      */
36822     cls: "x-small-editor x-tree-editor",
36823     /**
36824      * @cfg {Boolean} shim
36825      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
36826      */
36827     shim:false,
36828     // inherit
36829     shadow:"frame",
36830     /**
36831      * @cfg {Number} maxWidth
36832      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
36833      * the containing tree element's size, it will be automatically limited for you to the container width, taking
36834      * scroll and client offsets into account prior to each edit.
36835      */
36836     maxWidth: 250,
36837
36838     editDelay : 350,
36839
36840     // private
36841     fitToTree : function(ed, el){
36842         var td = this.tree.getTreeEl().dom, nd = el.dom;
36843         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
36844             td.scrollLeft = nd.offsetLeft;
36845         }
36846         var w = Math.min(
36847                 this.maxWidth,
36848                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
36849         this.setSize(w, '');
36850         
36851         return this.fireEvent('beforenodeedit', this, this.editNode);
36852         
36853     },
36854
36855     // private
36856     triggerEdit : function(node){
36857         this.completeEdit();
36858         this.editNode = node;
36859         this.startEdit(node.ui.textNode, node.text);
36860     },
36861
36862     // private
36863     bindScroll : function(){
36864         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
36865     },
36866
36867     // private
36868     beforeNodeClick : function(node, e){
36869         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
36870         this.lastClick = new Date();
36871         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
36872             e.stopEvent();
36873             this.triggerEdit(node);
36874             return false;
36875         }
36876         return true;
36877     },
36878
36879     // private
36880     updateNode : function(ed, value){
36881         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
36882         this.editNode.setText(value);
36883     },
36884
36885     // private
36886     onHide : function(){
36887         Roo.tree.TreeEditor.superclass.onHide.call(this);
36888         if(this.editNode){
36889             this.editNode.ui.focus();
36890         }
36891     },
36892
36893     // private
36894     onSpecialKey : function(field, e){
36895         var k = e.getKey();
36896         if(k == e.ESC){
36897             e.stopEvent();
36898             this.cancelEdit();
36899         }else if(k == e.ENTER && !e.hasModifier()){
36900             e.stopEvent();
36901             this.completeEdit();
36902         }
36903     }
36904 });//<Script type="text/javascript">
36905 /*
36906  * Based on:
36907  * Ext JS Library 1.1.1
36908  * Copyright(c) 2006-2007, Ext JS, LLC.
36909  *
36910  * Originally Released Under LGPL - original licence link has changed is not relivant.
36911  *
36912  * Fork - LGPL
36913  * <script type="text/javascript">
36914  */
36915  
36916 /**
36917  * Not documented??? - probably should be...
36918  */
36919
36920 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
36921     //focus: Roo.emptyFn, // prevent odd scrolling behavior
36922     
36923     renderElements : function(n, a, targetNode, bulkRender){
36924         //consel.log("renderElements?");
36925         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36926
36927         var t = n.getOwnerTree();
36928         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
36929         
36930         var cols = t.columns;
36931         var bw = t.borderWidth;
36932         var c = cols[0];
36933         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36934          var cb = typeof a.checked == "boolean";
36935         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36936         var colcls = 'x-t-' + tid + '-c0';
36937         var buf = [
36938             '<li class="x-tree-node">',
36939             
36940                 
36941                 '<div class="x-tree-node-el ', a.cls,'">',
36942                     // extran...
36943                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
36944                 
36945                 
36946                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
36947                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
36948                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
36949                            (a.icon ? ' x-tree-node-inline-icon' : ''),
36950                            (a.iconCls ? ' '+a.iconCls : ''),
36951                            '" unselectable="on" />',
36952                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
36953                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
36954                              
36955                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36956                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
36957                             '<span unselectable="on" qtip="' + tx + '">',
36958                              tx,
36959                              '</span></a>' ,
36960                     '</div>',
36961                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36962                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
36963                  ];
36964         for(var i = 1, len = cols.length; i < len; i++){
36965             c = cols[i];
36966             colcls = 'x-t-' + tid + '-c' +i;
36967             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36968             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
36969                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
36970                       "</div>");
36971          }
36972          
36973          buf.push(
36974             '</a>',
36975             '<div class="x-clear"></div></div>',
36976             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36977             "</li>");
36978         
36979         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36980             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36981                                 n.nextSibling.ui.getEl(), buf.join(""));
36982         }else{
36983             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36984         }
36985         var el = this.wrap.firstChild;
36986         this.elRow = el;
36987         this.elNode = el.firstChild;
36988         this.ranchor = el.childNodes[1];
36989         this.ctNode = this.wrap.childNodes[1];
36990         var cs = el.firstChild.childNodes;
36991         this.indentNode = cs[0];
36992         this.ecNode = cs[1];
36993         this.iconNode = cs[2];
36994         var index = 3;
36995         if(cb){
36996             this.checkbox = cs[3];
36997             index++;
36998         }
36999         this.anchor = cs[index];
37000         
37001         this.textNode = cs[index].firstChild;
37002         
37003         //el.on("click", this.onClick, this);
37004         //el.on("dblclick", this.onDblClick, this);
37005         
37006         
37007        // console.log(this);
37008     },
37009     initEvents : function(){
37010         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
37011         
37012             
37013         var a = this.ranchor;
37014
37015         var el = Roo.get(a);
37016
37017         if(Roo.isOpera){ // opera render bug ignores the CSS
37018             el.setStyle("text-decoration", "none");
37019         }
37020
37021         el.on("click", this.onClick, this);
37022         el.on("dblclick", this.onDblClick, this);
37023         el.on("contextmenu", this.onContextMenu, this);
37024         
37025     },
37026     
37027     /*onSelectedChange : function(state){
37028         if(state){
37029             this.focus();
37030             this.addClass("x-tree-selected");
37031         }else{
37032             //this.blur();
37033             this.removeClass("x-tree-selected");
37034         }
37035     },*/
37036     addClass : function(cls){
37037         if(this.elRow){
37038             Roo.fly(this.elRow).addClass(cls);
37039         }
37040         
37041     },
37042     
37043     
37044     removeClass : function(cls){
37045         if(this.elRow){
37046             Roo.fly(this.elRow).removeClass(cls);
37047         }
37048     }
37049
37050     
37051     
37052 });//<Script type="text/javascript">
37053
37054 /*
37055  * Based on:
37056  * Ext JS Library 1.1.1
37057  * Copyright(c) 2006-2007, Ext JS, LLC.
37058  *
37059  * Originally Released Under LGPL - original licence link has changed is not relivant.
37060  *
37061  * Fork - LGPL
37062  * <script type="text/javascript">
37063  */
37064  
37065
37066 /**
37067  * @class Roo.tree.ColumnTree
37068  * @extends Roo.data.TreePanel
37069  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
37070  * @cfg {int} borderWidth  compined right/left border allowance
37071  * @constructor
37072  * @param {String/HTMLElement/Element} el The container element
37073  * @param {Object} config
37074  */
37075 Roo.tree.ColumnTree =  function(el, config)
37076 {
37077    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
37078    this.addEvents({
37079         /**
37080         * @event resize
37081         * Fire this event on a container when it resizes
37082         * @param {int} w Width
37083         * @param {int} h Height
37084         */
37085        "resize" : true
37086     });
37087     this.on('resize', this.onResize, this);
37088 };
37089
37090 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37091     //lines:false,
37092     
37093     
37094     borderWidth: Roo.isBorderBox ? 0 : 2, 
37095     headEls : false,
37096     
37097     render : function(){
37098         // add the header.....
37099        
37100         Roo.tree.ColumnTree.superclass.render.apply(this);
37101         
37102         this.el.addClass('x-column-tree');
37103         
37104         this.headers = this.el.createChild(
37105             {cls:'x-tree-headers'},this.innerCt.dom);
37106    
37107         var cols = this.columns, c;
37108         var totalWidth = 0;
37109         this.headEls = [];
37110         var  len = cols.length;
37111         for(var i = 0; i < len; i++){
37112              c = cols[i];
37113              totalWidth += c.width;
37114             this.headEls.push(this.headers.createChild({
37115                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37116                  cn: {
37117                      cls:'x-tree-hd-text',
37118                      html: c.header
37119                  },
37120                  style:'width:'+(c.width-this.borderWidth)+'px;'
37121              }));
37122         }
37123         this.headers.createChild({cls:'x-clear'});
37124         // prevent floats from wrapping when clipped
37125         this.headers.setWidth(totalWidth);
37126         //this.innerCt.setWidth(totalWidth);
37127         this.innerCt.setStyle({ overflow: 'auto' });
37128         this.onResize(this.width, this.height);
37129              
37130         
37131     },
37132     onResize : function(w,h)
37133     {
37134         this.height = h;
37135         this.width = w;
37136         // resize cols..
37137         this.innerCt.setWidth(this.width);
37138         this.innerCt.setHeight(this.height-20);
37139         
37140         // headers...
37141         var cols = this.columns, c;
37142         var totalWidth = 0;
37143         var expEl = false;
37144         var len = cols.length;
37145         for(var i = 0; i < len; i++){
37146             c = cols[i];
37147             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37148                 // it's the expander..
37149                 expEl  = this.headEls[i];
37150                 continue;
37151             }
37152             totalWidth += c.width;
37153             
37154         }
37155         if (expEl) {
37156             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37157         }
37158         this.headers.setWidth(w-20);
37159
37160         
37161         
37162         
37163     }
37164 });
37165 /*
37166  * Based on:
37167  * Ext JS Library 1.1.1
37168  * Copyright(c) 2006-2007, Ext JS, LLC.
37169  *
37170  * Originally Released Under LGPL - original licence link has changed is not relivant.
37171  *
37172  * Fork - LGPL
37173  * <script type="text/javascript">
37174  */
37175  
37176 /**
37177  * @class Roo.menu.Menu
37178  * @extends Roo.util.Observable
37179  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37180  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37181  * @constructor
37182  * Creates a new Menu
37183  * @param {Object} config Configuration options
37184  */
37185 Roo.menu.Menu = function(config){
37186     Roo.apply(this, config);
37187     this.id = this.id || Roo.id();
37188     this.addEvents({
37189         /**
37190          * @event beforeshow
37191          * Fires before this menu is displayed
37192          * @param {Roo.menu.Menu} this
37193          */
37194         beforeshow : true,
37195         /**
37196          * @event beforehide
37197          * Fires before this menu is hidden
37198          * @param {Roo.menu.Menu} this
37199          */
37200         beforehide : true,
37201         /**
37202          * @event show
37203          * Fires after this menu is displayed
37204          * @param {Roo.menu.Menu} this
37205          */
37206         show : true,
37207         /**
37208          * @event hide
37209          * Fires after this menu is hidden
37210          * @param {Roo.menu.Menu} this
37211          */
37212         hide : true,
37213         /**
37214          * @event click
37215          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37216          * @param {Roo.menu.Menu} this
37217          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37218          * @param {Roo.EventObject} e
37219          */
37220         click : true,
37221         /**
37222          * @event mouseover
37223          * Fires when the mouse is hovering over this menu
37224          * @param {Roo.menu.Menu} this
37225          * @param {Roo.EventObject} e
37226          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37227          */
37228         mouseover : true,
37229         /**
37230          * @event mouseout
37231          * Fires when the mouse exits this menu
37232          * @param {Roo.menu.Menu} this
37233          * @param {Roo.EventObject} e
37234          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37235          */
37236         mouseout : true,
37237         /**
37238          * @event itemclick
37239          * Fires when a menu item contained in this menu is clicked
37240          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37241          * @param {Roo.EventObject} e
37242          */
37243         itemclick: true
37244     });
37245     if (this.registerMenu) {
37246         Roo.menu.MenuMgr.register(this);
37247     }
37248     
37249     var mis = this.items;
37250     this.items = new Roo.util.MixedCollection();
37251     if(mis){
37252         this.add.apply(this, mis);
37253     }
37254 };
37255
37256 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37257     /**
37258      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37259      */
37260     minWidth : 120,
37261     /**
37262      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37263      * for bottom-right shadow (defaults to "sides")
37264      */
37265     shadow : "sides",
37266     /**
37267      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37268      * this menu (defaults to "tl-tr?")
37269      */
37270     subMenuAlign : "tl-tr?",
37271     /**
37272      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37273      * relative to its element of origin (defaults to "tl-bl?")
37274      */
37275     defaultAlign : "tl-bl?",
37276     /**
37277      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37278      */
37279     allowOtherMenus : false,
37280     /**
37281      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37282      */
37283     registerMenu : true,
37284
37285     hidden:true,
37286
37287     // private
37288     render : function(){
37289         if(this.el){
37290             return;
37291         }
37292         var el = this.el = new Roo.Layer({
37293             cls: "x-menu",
37294             shadow:this.shadow,
37295             constrain: false,
37296             parentEl: this.parentEl || document.body,
37297             zindex:15000
37298         });
37299
37300         this.keyNav = new Roo.menu.MenuNav(this);
37301
37302         if(this.plain){
37303             el.addClass("x-menu-plain");
37304         }
37305         if(this.cls){
37306             el.addClass(this.cls);
37307         }
37308         // generic focus element
37309         this.focusEl = el.createChild({
37310             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37311         });
37312         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37313         //disabling touch- as it's causing issues ..
37314         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37315         ul.on('click'   , this.onClick, this);
37316         
37317         
37318         ul.on("mouseover", this.onMouseOver, this);
37319         ul.on("mouseout", this.onMouseOut, this);
37320         this.items.each(function(item){
37321             if (item.hidden) {
37322                 return;
37323             }
37324             
37325             var li = document.createElement("li");
37326             li.className = "x-menu-list-item";
37327             ul.dom.appendChild(li);
37328             item.render(li, this);
37329         }, this);
37330         this.ul = ul;
37331         this.autoWidth();
37332     },
37333
37334     // private
37335     autoWidth : function(){
37336         var el = this.el, ul = this.ul;
37337         if(!el){
37338             return;
37339         }
37340         var w = this.width;
37341         if(w){
37342             el.setWidth(w);
37343         }else if(Roo.isIE){
37344             el.setWidth(this.minWidth);
37345             var t = el.dom.offsetWidth; // force recalc
37346             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37347         }
37348     },
37349
37350     // private
37351     delayAutoWidth : function(){
37352         if(this.rendered){
37353             if(!this.awTask){
37354                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37355             }
37356             this.awTask.delay(20);
37357         }
37358     },
37359
37360     // private
37361     findTargetItem : function(e){
37362         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37363         if(t && t.menuItemId){
37364             return this.items.get(t.menuItemId);
37365         }
37366     },
37367
37368     // private
37369     onClick : function(e){
37370         Roo.log("menu.onClick");
37371         var t = this.findTargetItem(e);
37372         if(!t){
37373             return;
37374         }
37375         Roo.log(e);
37376         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37377             if(t == this.activeItem && t.shouldDeactivate(e)){
37378                 this.activeItem.deactivate();
37379                 delete this.activeItem;
37380                 return;
37381             }
37382             if(t.canActivate){
37383                 this.setActiveItem(t, true);
37384             }
37385             return;
37386             
37387             
37388         }
37389         
37390         t.onClick(e);
37391         this.fireEvent("click", this, t, e);
37392     },
37393
37394     // private
37395     setActiveItem : function(item, autoExpand){
37396         if(item != this.activeItem){
37397             if(this.activeItem){
37398                 this.activeItem.deactivate();
37399             }
37400             this.activeItem = item;
37401             item.activate(autoExpand);
37402         }else if(autoExpand){
37403             item.expandMenu();
37404         }
37405     },
37406
37407     // private
37408     tryActivate : function(start, step){
37409         var items = this.items;
37410         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37411             var item = items.get(i);
37412             if(!item.disabled && item.canActivate){
37413                 this.setActiveItem(item, false);
37414                 return item;
37415             }
37416         }
37417         return false;
37418     },
37419
37420     // private
37421     onMouseOver : function(e){
37422         var t;
37423         if(t = this.findTargetItem(e)){
37424             if(t.canActivate && !t.disabled){
37425                 this.setActiveItem(t, true);
37426             }
37427         }
37428         this.fireEvent("mouseover", this, e, t);
37429     },
37430
37431     // private
37432     onMouseOut : function(e){
37433         var t;
37434         if(t = this.findTargetItem(e)){
37435             if(t == this.activeItem && t.shouldDeactivate(e)){
37436                 this.activeItem.deactivate();
37437                 delete this.activeItem;
37438             }
37439         }
37440         this.fireEvent("mouseout", this, e, t);
37441     },
37442
37443     /**
37444      * Read-only.  Returns true if the menu is currently displayed, else false.
37445      * @type Boolean
37446      */
37447     isVisible : function(){
37448         return this.el && !this.hidden;
37449     },
37450
37451     /**
37452      * Displays this menu relative to another element
37453      * @param {String/HTMLElement/Roo.Element} element The element to align to
37454      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37455      * the element (defaults to this.defaultAlign)
37456      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37457      */
37458     show : function(el, pos, parentMenu){
37459         this.parentMenu = parentMenu;
37460         if(!this.el){
37461             this.render();
37462         }
37463         this.fireEvent("beforeshow", this);
37464         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37465     },
37466
37467     /**
37468      * Displays this menu at a specific xy position
37469      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37470      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37471      */
37472     showAt : function(xy, parentMenu, /* private: */_e){
37473         this.parentMenu = parentMenu;
37474         if(!this.el){
37475             this.render();
37476         }
37477         if(_e !== false){
37478             this.fireEvent("beforeshow", this);
37479             xy = this.el.adjustForConstraints(xy);
37480         }
37481         this.el.setXY(xy);
37482         this.el.show();
37483         this.hidden = false;
37484         this.focus();
37485         this.fireEvent("show", this);
37486     },
37487
37488     focus : function(){
37489         if(!this.hidden){
37490             this.doFocus.defer(50, this);
37491         }
37492     },
37493
37494     doFocus : function(){
37495         if(!this.hidden){
37496             this.focusEl.focus();
37497         }
37498     },
37499
37500     /**
37501      * Hides this menu and optionally all parent menus
37502      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37503      */
37504     hide : function(deep){
37505         if(this.el && this.isVisible()){
37506             this.fireEvent("beforehide", this);
37507             if(this.activeItem){
37508                 this.activeItem.deactivate();
37509                 this.activeItem = null;
37510             }
37511             this.el.hide();
37512             this.hidden = true;
37513             this.fireEvent("hide", this);
37514         }
37515         if(deep === true && this.parentMenu){
37516             this.parentMenu.hide(true);
37517         }
37518     },
37519
37520     /**
37521      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37522      * Any of the following are valid:
37523      * <ul>
37524      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37525      * <li>An HTMLElement object which will be converted to a menu item</li>
37526      * <li>A menu item config object that will be created as a new menu item</li>
37527      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37528      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37529      * </ul>
37530      * Usage:
37531      * <pre><code>
37532 // Create the menu
37533 var menu = new Roo.menu.Menu();
37534
37535 // Create a menu item to add by reference
37536 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37537
37538 // Add a bunch of items at once using different methods.
37539 // Only the last item added will be returned.
37540 var item = menu.add(
37541     menuItem,                // add existing item by ref
37542     'Dynamic Item',          // new TextItem
37543     '-',                     // new separator
37544     { text: 'Config Item' }  // new item by config
37545 );
37546 </code></pre>
37547      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37548      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37549      */
37550     add : function(){
37551         var a = arguments, l = a.length, item;
37552         for(var i = 0; i < l; i++){
37553             var el = a[i];
37554             if ((typeof(el) == "object") && el.xtype && el.xns) {
37555                 el = Roo.factory(el, Roo.menu);
37556             }
37557             
37558             if(el.render){ // some kind of Item
37559                 item = this.addItem(el);
37560             }else if(typeof el == "string"){ // string
37561                 if(el == "separator" || el == "-"){
37562                     item = this.addSeparator();
37563                 }else{
37564                     item = this.addText(el);
37565                 }
37566             }else if(el.tagName || el.el){ // element
37567                 item = this.addElement(el);
37568             }else if(typeof el == "object"){ // must be menu item config?
37569                 item = this.addMenuItem(el);
37570             }
37571         }
37572         return item;
37573     },
37574
37575     /**
37576      * Returns this menu's underlying {@link Roo.Element} object
37577      * @return {Roo.Element} The element
37578      */
37579     getEl : function(){
37580         if(!this.el){
37581             this.render();
37582         }
37583         return this.el;
37584     },
37585
37586     /**
37587      * Adds a separator bar to the menu
37588      * @return {Roo.menu.Item} The menu item that was added
37589      */
37590     addSeparator : function(){
37591         return this.addItem(new Roo.menu.Separator());
37592     },
37593
37594     /**
37595      * Adds an {@link Roo.Element} object to the menu
37596      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37597      * @return {Roo.menu.Item} The menu item that was added
37598      */
37599     addElement : function(el){
37600         return this.addItem(new Roo.menu.BaseItem(el));
37601     },
37602
37603     /**
37604      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37605      * @param {Roo.menu.Item} item The menu item to add
37606      * @return {Roo.menu.Item} The menu item that was added
37607      */
37608     addItem : function(item){
37609         this.items.add(item);
37610         if(this.ul){
37611             var li = document.createElement("li");
37612             li.className = "x-menu-list-item";
37613             this.ul.dom.appendChild(li);
37614             item.render(li, this);
37615             this.delayAutoWidth();
37616         }
37617         return item;
37618     },
37619
37620     /**
37621      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37622      * @param {Object} config A MenuItem config object
37623      * @return {Roo.menu.Item} The menu item that was added
37624      */
37625     addMenuItem : function(config){
37626         if(!(config instanceof Roo.menu.Item)){
37627             if(typeof config.checked == "boolean"){ // must be check menu item config?
37628                 config = new Roo.menu.CheckItem(config);
37629             }else{
37630                 config = new Roo.menu.Item(config);
37631             }
37632         }
37633         return this.addItem(config);
37634     },
37635
37636     /**
37637      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37638      * @param {String} text The text to display in the menu item
37639      * @return {Roo.menu.Item} The menu item that was added
37640      */
37641     addText : function(text){
37642         return this.addItem(new Roo.menu.TextItem({ text : text }));
37643     },
37644
37645     /**
37646      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37647      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37648      * @param {Roo.menu.Item} item The menu item to add
37649      * @return {Roo.menu.Item} The menu item that was added
37650      */
37651     insert : function(index, item){
37652         this.items.insert(index, item);
37653         if(this.ul){
37654             var li = document.createElement("li");
37655             li.className = "x-menu-list-item";
37656             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37657             item.render(li, this);
37658             this.delayAutoWidth();
37659         }
37660         return item;
37661     },
37662
37663     /**
37664      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37665      * @param {Roo.menu.Item} item The menu item to remove
37666      */
37667     remove : function(item){
37668         this.items.removeKey(item.id);
37669         item.destroy();
37670     },
37671
37672     /**
37673      * Removes and destroys all items in the menu
37674      */
37675     removeAll : function(){
37676         var f;
37677         while(f = this.items.first()){
37678             this.remove(f);
37679         }
37680     }
37681 });
37682
37683 // MenuNav is a private utility class used internally by the Menu
37684 Roo.menu.MenuNav = function(menu){
37685     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37686     this.scope = this.menu = menu;
37687 };
37688
37689 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37690     doRelay : function(e, h){
37691         var k = e.getKey();
37692         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37693             this.menu.tryActivate(0, 1);
37694             return false;
37695         }
37696         return h.call(this.scope || this, e, this.menu);
37697     },
37698
37699     up : function(e, m){
37700         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37701             m.tryActivate(m.items.length-1, -1);
37702         }
37703     },
37704
37705     down : function(e, m){
37706         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37707             m.tryActivate(0, 1);
37708         }
37709     },
37710
37711     right : function(e, m){
37712         if(m.activeItem){
37713             m.activeItem.expandMenu(true);
37714         }
37715     },
37716
37717     left : function(e, m){
37718         m.hide();
37719         if(m.parentMenu && m.parentMenu.activeItem){
37720             m.parentMenu.activeItem.activate();
37721         }
37722     },
37723
37724     enter : function(e, m){
37725         if(m.activeItem){
37726             e.stopPropagation();
37727             m.activeItem.onClick(e);
37728             m.fireEvent("click", this, m.activeItem);
37729             return true;
37730         }
37731     }
37732 });/*
37733  * Based on:
37734  * Ext JS Library 1.1.1
37735  * Copyright(c) 2006-2007, Ext JS, LLC.
37736  *
37737  * Originally Released Under LGPL - original licence link has changed is not relivant.
37738  *
37739  * Fork - LGPL
37740  * <script type="text/javascript">
37741  */
37742  
37743 /**
37744  * @class Roo.menu.MenuMgr
37745  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37746  * @singleton
37747  */
37748 Roo.menu.MenuMgr = function(){
37749    var menus, active, groups = {}, attached = false, lastShow = new Date();
37750
37751    // private - called when first menu is created
37752    function init(){
37753        menus = {};
37754        active = new Roo.util.MixedCollection();
37755        Roo.get(document).addKeyListener(27, function(){
37756            if(active.length > 0){
37757                hideAll();
37758            }
37759        });
37760    }
37761
37762    // private
37763    function hideAll(){
37764        if(active && active.length > 0){
37765            var c = active.clone();
37766            c.each(function(m){
37767                m.hide();
37768            });
37769        }
37770    }
37771
37772    // private
37773    function onHide(m){
37774        active.remove(m);
37775        if(active.length < 1){
37776            Roo.get(document).un("mousedown", onMouseDown);
37777            attached = false;
37778        }
37779    }
37780
37781    // private
37782    function onShow(m){
37783        var last = active.last();
37784        lastShow = new Date();
37785        active.add(m);
37786        if(!attached){
37787            Roo.get(document).on("mousedown", onMouseDown);
37788            attached = true;
37789        }
37790        if(m.parentMenu){
37791           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37792           m.parentMenu.activeChild = m;
37793        }else if(last && last.isVisible()){
37794           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37795        }
37796    }
37797
37798    // private
37799    function onBeforeHide(m){
37800        if(m.activeChild){
37801            m.activeChild.hide();
37802        }
37803        if(m.autoHideTimer){
37804            clearTimeout(m.autoHideTimer);
37805            delete m.autoHideTimer;
37806        }
37807    }
37808
37809    // private
37810    function onBeforeShow(m){
37811        var pm = m.parentMenu;
37812        if(!pm && !m.allowOtherMenus){
37813            hideAll();
37814        }else if(pm && pm.activeChild && active != m){
37815            pm.activeChild.hide();
37816        }
37817    }
37818
37819    // private
37820    function onMouseDown(e){
37821        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
37822            hideAll();
37823        }
37824    }
37825
37826    // private
37827    function onBeforeCheck(mi, state){
37828        if(state){
37829            var g = groups[mi.group];
37830            for(var i = 0, l = g.length; i < l; i++){
37831                if(g[i] != mi){
37832                    g[i].setChecked(false);
37833                }
37834            }
37835        }
37836    }
37837
37838    return {
37839
37840        /**
37841         * Hides all menus that are currently visible
37842         */
37843        hideAll : function(){
37844             hideAll();  
37845        },
37846
37847        // private
37848        register : function(menu){
37849            if(!menus){
37850                init();
37851            }
37852            menus[menu.id] = menu;
37853            menu.on("beforehide", onBeforeHide);
37854            menu.on("hide", onHide);
37855            menu.on("beforeshow", onBeforeShow);
37856            menu.on("show", onShow);
37857            var g = menu.group;
37858            if(g && menu.events["checkchange"]){
37859                if(!groups[g]){
37860                    groups[g] = [];
37861                }
37862                groups[g].push(menu);
37863                menu.on("checkchange", onCheck);
37864            }
37865        },
37866
37867         /**
37868          * Returns a {@link Roo.menu.Menu} object
37869          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
37870          * be used to generate and return a new Menu instance.
37871          */
37872        get : function(menu){
37873            if(typeof menu == "string"){ // menu id
37874                return menus[menu];
37875            }else if(menu.events){  // menu instance
37876                return menu;
37877            }else if(typeof menu.length == 'number'){ // array of menu items?
37878                return new Roo.menu.Menu({items:menu});
37879            }else{ // otherwise, must be a config
37880                return new Roo.menu.Menu(menu);
37881            }
37882        },
37883
37884        // private
37885        unregister : function(menu){
37886            delete menus[menu.id];
37887            menu.un("beforehide", onBeforeHide);
37888            menu.un("hide", onHide);
37889            menu.un("beforeshow", onBeforeShow);
37890            menu.un("show", onShow);
37891            var g = menu.group;
37892            if(g && menu.events["checkchange"]){
37893                groups[g].remove(menu);
37894                menu.un("checkchange", onCheck);
37895            }
37896        },
37897
37898        // private
37899        registerCheckable : function(menuItem){
37900            var g = menuItem.group;
37901            if(g){
37902                if(!groups[g]){
37903                    groups[g] = [];
37904                }
37905                groups[g].push(menuItem);
37906                menuItem.on("beforecheckchange", onBeforeCheck);
37907            }
37908        },
37909
37910        // private
37911        unregisterCheckable : function(menuItem){
37912            var g = menuItem.group;
37913            if(g){
37914                groups[g].remove(menuItem);
37915                menuItem.un("beforecheckchange", onBeforeCheck);
37916            }
37917        }
37918    };
37919 }();/*
37920  * Based on:
37921  * Ext JS Library 1.1.1
37922  * Copyright(c) 2006-2007, Ext JS, LLC.
37923  *
37924  * Originally Released Under LGPL - original licence link has changed is not relivant.
37925  *
37926  * Fork - LGPL
37927  * <script type="text/javascript">
37928  */
37929  
37930
37931 /**
37932  * @class Roo.menu.BaseItem
37933  * @extends Roo.Component
37934  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
37935  * management and base configuration options shared by all menu components.
37936  * @constructor
37937  * Creates a new BaseItem
37938  * @param {Object} config Configuration options
37939  */
37940 Roo.menu.BaseItem = function(config){
37941     Roo.menu.BaseItem.superclass.constructor.call(this, config);
37942
37943     this.addEvents({
37944         /**
37945          * @event click
37946          * Fires when this item is clicked
37947          * @param {Roo.menu.BaseItem} this
37948          * @param {Roo.EventObject} e
37949          */
37950         click: true,
37951         /**
37952          * @event activate
37953          * Fires when this item is activated
37954          * @param {Roo.menu.BaseItem} this
37955          */
37956         activate : true,
37957         /**
37958          * @event deactivate
37959          * Fires when this item is deactivated
37960          * @param {Roo.menu.BaseItem} this
37961          */
37962         deactivate : true
37963     });
37964
37965     if(this.handler){
37966         this.on("click", this.handler, this.scope, true);
37967     }
37968 };
37969
37970 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
37971     /**
37972      * @cfg {Function} handler
37973      * A function that will handle the click event of this menu item (defaults to undefined)
37974      */
37975     /**
37976      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
37977      */
37978     canActivate : false,
37979     
37980      /**
37981      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
37982      */
37983     hidden: false,
37984     
37985     /**
37986      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
37987      */
37988     activeClass : "x-menu-item-active",
37989     /**
37990      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
37991      */
37992     hideOnClick : true,
37993     /**
37994      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
37995      */
37996     hideDelay : 100,
37997
37998     // private
37999     ctype: "Roo.menu.BaseItem",
38000
38001     // private
38002     actionMode : "container",
38003
38004     // private
38005     render : function(container, parentMenu){
38006         this.parentMenu = parentMenu;
38007         Roo.menu.BaseItem.superclass.render.call(this, container);
38008         this.container.menuItemId = this.id;
38009     },
38010
38011     // private
38012     onRender : function(container, position){
38013         this.el = Roo.get(this.el);
38014         container.dom.appendChild(this.el.dom);
38015     },
38016
38017     // private
38018     onClick : function(e){
38019         if(!this.disabled && this.fireEvent("click", this, e) !== false
38020                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
38021             this.handleClick(e);
38022         }else{
38023             e.stopEvent();
38024         }
38025     },
38026
38027     // private
38028     activate : function(){
38029         if(this.disabled){
38030             return false;
38031         }
38032         var li = this.container;
38033         li.addClass(this.activeClass);
38034         this.region = li.getRegion().adjust(2, 2, -2, -2);
38035         this.fireEvent("activate", this);
38036         return true;
38037     },
38038
38039     // private
38040     deactivate : function(){
38041         this.container.removeClass(this.activeClass);
38042         this.fireEvent("deactivate", this);
38043     },
38044
38045     // private
38046     shouldDeactivate : function(e){
38047         return !this.region || !this.region.contains(e.getPoint());
38048     },
38049
38050     // private
38051     handleClick : function(e){
38052         if(this.hideOnClick){
38053             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
38054         }
38055     },
38056
38057     // private
38058     expandMenu : function(autoActivate){
38059         // do nothing
38060     },
38061
38062     // private
38063     hideMenu : function(){
38064         // do nothing
38065     }
38066 });/*
38067  * Based on:
38068  * Ext JS Library 1.1.1
38069  * Copyright(c) 2006-2007, Ext JS, LLC.
38070  *
38071  * Originally Released Under LGPL - original licence link has changed is not relivant.
38072  *
38073  * Fork - LGPL
38074  * <script type="text/javascript">
38075  */
38076  
38077 /**
38078  * @class Roo.menu.Adapter
38079  * @extends Roo.menu.BaseItem
38080  * 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.
38081  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38082  * @constructor
38083  * Creates a new Adapter
38084  * @param {Object} config Configuration options
38085  */
38086 Roo.menu.Adapter = function(component, config){
38087     Roo.menu.Adapter.superclass.constructor.call(this, config);
38088     this.component = component;
38089 };
38090 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38091     // private
38092     canActivate : true,
38093
38094     // private
38095     onRender : function(container, position){
38096         this.component.render(container);
38097         this.el = this.component.getEl();
38098     },
38099
38100     // private
38101     activate : function(){
38102         if(this.disabled){
38103             return false;
38104         }
38105         this.component.focus();
38106         this.fireEvent("activate", this);
38107         return true;
38108     },
38109
38110     // private
38111     deactivate : function(){
38112         this.fireEvent("deactivate", this);
38113     },
38114
38115     // private
38116     disable : function(){
38117         this.component.disable();
38118         Roo.menu.Adapter.superclass.disable.call(this);
38119     },
38120
38121     // private
38122     enable : function(){
38123         this.component.enable();
38124         Roo.menu.Adapter.superclass.enable.call(this);
38125     }
38126 });/*
38127  * Based on:
38128  * Ext JS Library 1.1.1
38129  * Copyright(c) 2006-2007, Ext JS, LLC.
38130  *
38131  * Originally Released Under LGPL - original licence link has changed is not relivant.
38132  *
38133  * Fork - LGPL
38134  * <script type="text/javascript">
38135  */
38136
38137 /**
38138  * @class Roo.menu.TextItem
38139  * @extends Roo.menu.BaseItem
38140  * Adds a static text string to a menu, usually used as either a heading or group separator.
38141  * Note: old style constructor with text is still supported.
38142  * 
38143  * @constructor
38144  * Creates a new TextItem
38145  * @param {Object} cfg Configuration
38146  */
38147 Roo.menu.TextItem = function(cfg){
38148     if (typeof(cfg) == 'string') {
38149         this.text = cfg;
38150     } else {
38151         Roo.apply(this,cfg);
38152     }
38153     
38154     Roo.menu.TextItem.superclass.constructor.call(this);
38155 };
38156
38157 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38158     /**
38159      * @cfg {Boolean} text Text to show on item.
38160      */
38161     text : '',
38162     
38163     /**
38164      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38165      */
38166     hideOnClick : false,
38167     /**
38168      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38169      */
38170     itemCls : "x-menu-text",
38171
38172     // private
38173     onRender : function(){
38174         var s = document.createElement("span");
38175         s.className = this.itemCls;
38176         s.innerHTML = this.text;
38177         this.el = s;
38178         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38179     }
38180 });/*
38181  * Based on:
38182  * Ext JS Library 1.1.1
38183  * Copyright(c) 2006-2007, Ext JS, LLC.
38184  *
38185  * Originally Released Under LGPL - original licence link has changed is not relivant.
38186  *
38187  * Fork - LGPL
38188  * <script type="text/javascript">
38189  */
38190
38191 /**
38192  * @class Roo.menu.Separator
38193  * @extends Roo.menu.BaseItem
38194  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38195  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38196  * @constructor
38197  * @param {Object} config Configuration options
38198  */
38199 Roo.menu.Separator = function(config){
38200     Roo.menu.Separator.superclass.constructor.call(this, config);
38201 };
38202
38203 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38204     /**
38205      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38206      */
38207     itemCls : "x-menu-sep",
38208     /**
38209      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38210      */
38211     hideOnClick : false,
38212
38213     // private
38214     onRender : function(li){
38215         var s = document.createElement("span");
38216         s.className = this.itemCls;
38217         s.innerHTML = "&#160;";
38218         this.el = s;
38219         li.addClass("x-menu-sep-li");
38220         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38221     }
38222 });/*
38223  * Based on:
38224  * Ext JS Library 1.1.1
38225  * Copyright(c) 2006-2007, Ext JS, LLC.
38226  *
38227  * Originally Released Under LGPL - original licence link has changed is not relivant.
38228  *
38229  * Fork - LGPL
38230  * <script type="text/javascript">
38231  */
38232 /**
38233  * @class Roo.menu.Item
38234  * @extends Roo.menu.BaseItem
38235  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38236  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38237  * activation and click handling.
38238  * @constructor
38239  * Creates a new Item
38240  * @param {Object} config Configuration options
38241  */
38242 Roo.menu.Item = function(config){
38243     Roo.menu.Item.superclass.constructor.call(this, config);
38244     if(this.menu){
38245         this.menu = Roo.menu.MenuMgr.get(this.menu);
38246     }
38247 };
38248 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38249     
38250     /**
38251      * @cfg {String} text
38252      * The text to show on the menu item.
38253      */
38254     text: '',
38255      /**
38256      * @cfg {String} HTML to render in menu
38257      * The text to show on the menu item (HTML version).
38258      */
38259     html: '',
38260     /**
38261      * @cfg {String} icon
38262      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38263      */
38264     icon: undefined,
38265     /**
38266      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38267      */
38268     itemCls : "x-menu-item",
38269     /**
38270      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38271      */
38272     canActivate : true,
38273     /**
38274      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38275      */
38276     showDelay: 200,
38277     // doc'd in BaseItem
38278     hideDelay: 200,
38279
38280     // private
38281     ctype: "Roo.menu.Item",
38282     
38283     // private
38284     onRender : function(container, position){
38285         var el = document.createElement("a");
38286         el.hideFocus = true;
38287         el.unselectable = "on";
38288         el.href = this.href || "#";
38289         if(this.hrefTarget){
38290             el.target = this.hrefTarget;
38291         }
38292         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38293         
38294         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38295         
38296         el.innerHTML = String.format(
38297                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38298                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38299         this.el = el;
38300         Roo.menu.Item.superclass.onRender.call(this, container, position);
38301     },
38302
38303     /**
38304      * Sets the text to display in this menu item
38305      * @param {String} text The text to display
38306      * @param {Boolean} isHTML true to indicate text is pure html.
38307      */
38308     setText : function(text, isHTML){
38309         if (isHTML) {
38310             this.html = text;
38311         } else {
38312             this.text = text;
38313             this.html = '';
38314         }
38315         if(this.rendered){
38316             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38317      
38318             this.el.update(String.format(
38319                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38320                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38321             this.parentMenu.autoWidth();
38322         }
38323     },
38324
38325     // private
38326     handleClick : function(e){
38327         if(!this.href){ // if no link defined, stop the event automatically
38328             e.stopEvent();
38329         }
38330         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38331     },
38332
38333     // private
38334     activate : function(autoExpand){
38335         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38336             this.focus();
38337             if(autoExpand){
38338                 this.expandMenu();
38339             }
38340         }
38341         return true;
38342     },
38343
38344     // private
38345     shouldDeactivate : function(e){
38346         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38347             if(this.menu && this.menu.isVisible()){
38348                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38349             }
38350             return true;
38351         }
38352         return false;
38353     },
38354
38355     // private
38356     deactivate : function(){
38357         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38358         this.hideMenu();
38359     },
38360
38361     // private
38362     expandMenu : function(autoActivate){
38363         if(!this.disabled && this.menu){
38364             clearTimeout(this.hideTimer);
38365             delete this.hideTimer;
38366             if(!this.menu.isVisible() && !this.showTimer){
38367                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38368             }else if (this.menu.isVisible() && autoActivate){
38369                 this.menu.tryActivate(0, 1);
38370             }
38371         }
38372     },
38373
38374     // private
38375     deferExpand : function(autoActivate){
38376         delete this.showTimer;
38377         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38378         if(autoActivate){
38379             this.menu.tryActivate(0, 1);
38380         }
38381     },
38382
38383     // private
38384     hideMenu : function(){
38385         clearTimeout(this.showTimer);
38386         delete this.showTimer;
38387         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38388             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38389         }
38390     },
38391
38392     // private
38393     deferHide : function(){
38394         delete this.hideTimer;
38395         this.menu.hide();
38396     }
38397 });/*
38398  * Based on:
38399  * Ext JS Library 1.1.1
38400  * Copyright(c) 2006-2007, Ext JS, LLC.
38401  *
38402  * Originally Released Under LGPL - original licence link has changed is not relivant.
38403  *
38404  * Fork - LGPL
38405  * <script type="text/javascript">
38406  */
38407  
38408 /**
38409  * @class Roo.menu.CheckItem
38410  * @extends Roo.menu.Item
38411  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38412  * @constructor
38413  * Creates a new CheckItem
38414  * @param {Object} config Configuration options
38415  */
38416 Roo.menu.CheckItem = function(config){
38417     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38418     this.addEvents({
38419         /**
38420          * @event beforecheckchange
38421          * Fires before the checked value is set, providing an opportunity to cancel if needed
38422          * @param {Roo.menu.CheckItem} this
38423          * @param {Boolean} checked The new checked value that will be set
38424          */
38425         "beforecheckchange" : true,
38426         /**
38427          * @event checkchange
38428          * Fires after the checked value has been set
38429          * @param {Roo.menu.CheckItem} this
38430          * @param {Boolean} checked The checked value that was set
38431          */
38432         "checkchange" : true
38433     });
38434     if(this.checkHandler){
38435         this.on('checkchange', this.checkHandler, this.scope);
38436     }
38437 };
38438 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38439     /**
38440      * @cfg {String} group
38441      * All check items with the same group name will automatically be grouped into a single-select
38442      * radio button group (defaults to '')
38443      */
38444     /**
38445      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38446      */
38447     itemCls : "x-menu-item x-menu-check-item",
38448     /**
38449      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38450      */
38451     groupClass : "x-menu-group-item",
38452
38453     /**
38454      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38455      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38456      * initialized with checked = true will be rendered as checked.
38457      */
38458     checked: false,
38459
38460     // private
38461     ctype: "Roo.menu.CheckItem",
38462
38463     // private
38464     onRender : function(c){
38465         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38466         if(this.group){
38467             this.el.addClass(this.groupClass);
38468         }
38469         Roo.menu.MenuMgr.registerCheckable(this);
38470         if(this.checked){
38471             this.checked = false;
38472             this.setChecked(true, true);
38473         }
38474     },
38475
38476     // private
38477     destroy : function(){
38478         if(this.rendered){
38479             Roo.menu.MenuMgr.unregisterCheckable(this);
38480         }
38481         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38482     },
38483
38484     /**
38485      * Set the checked state of this item
38486      * @param {Boolean} checked The new checked value
38487      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38488      */
38489     setChecked : function(state, suppressEvent){
38490         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38491             if(this.container){
38492                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38493             }
38494             this.checked = state;
38495             if(suppressEvent !== true){
38496                 this.fireEvent("checkchange", this, state);
38497             }
38498         }
38499     },
38500
38501     // private
38502     handleClick : function(e){
38503        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38504            this.setChecked(!this.checked);
38505        }
38506        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38507     }
38508 });/*
38509  * Based on:
38510  * Ext JS Library 1.1.1
38511  * Copyright(c) 2006-2007, Ext JS, LLC.
38512  *
38513  * Originally Released Under LGPL - original licence link has changed is not relivant.
38514  *
38515  * Fork - LGPL
38516  * <script type="text/javascript">
38517  */
38518  
38519 /**
38520  * @class Roo.menu.DateItem
38521  * @extends Roo.menu.Adapter
38522  * A menu item that wraps the {@link Roo.DatPicker} component.
38523  * @constructor
38524  * Creates a new DateItem
38525  * @param {Object} config Configuration options
38526  */
38527 Roo.menu.DateItem = function(config){
38528     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38529     /** The Roo.DatePicker object @type Roo.DatePicker */
38530     this.picker = this.component;
38531     this.addEvents({select: true});
38532     
38533     this.picker.on("render", function(picker){
38534         picker.getEl().swallowEvent("click");
38535         picker.container.addClass("x-menu-date-item");
38536     });
38537
38538     this.picker.on("select", this.onSelect, this);
38539 };
38540
38541 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38542     // private
38543     onSelect : function(picker, date){
38544         this.fireEvent("select", this, date, picker);
38545         Roo.menu.DateItem.superclass.handleClick.call(this);
38546     }
38547 });/*
38548  * Based on:
38549  * Ext JS Library 1.1.1
38550  * Copyright(c) 2006-2007, Ext JS, LLC.
38551  *
38552  * Originally Released Under LGPL - original licence link has changed is not relivant.
38553  *
38554  * Fork - LGPL
38555  * <script type="text/javascript">
38556  */
38557  
38558 /**
38559  * @class Roo.menu.ColorItem
38560  * @extends Roo.menu.Adapter
38561  * A menu item that wraps the {@link Roo.ColorPalette} component.
38562  * @constructor
38563  * Creates a new ColorItem
38564  * @param {Object} config Configuration options
38565  */
38566 Roo.menu.ColorItem = function(config){
38567     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38568     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38569     this.palette = this.component;
38570     this.relayEvents(this.palette, ["select"]);
38571     if(this.selectHandler){
38572         this.on('select', this.selectHandler, this.scope);
38573     }
38574 };
38575 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38576  * Based on:
38577  * Ext JS Library 1.1.1
38578  * Copyright(c) 2006-2007, Ext JS, LLC.
38579  *
38580  * Originally Released Under LGPL - original licence link has changed is not relivant.
38581  *
38582  * Fork - LGPL
38583  * <script type="text/javascript">
38584  */
38585  
38586
38587 /**
38588  * @class Roo.menu.DateMenu
38589  * @extends Roo.menu.Menu
38590  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38591  * @constructor
38592  * Creates a new DateMenu
38593  * @param {Object} config Configuration options
38594  */
38595 Roo.menu.DateMenu = function(config){
38596     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38597     this.plain = true;
38598     var di = new Roo.menu.DateItem(config);
38599     this.add(di);
38600     /**
38601      * The {@link Roo.DatePicker} instance for this DateMenu
38602      * @type DatePicker
38603      */
38604     this.picker = di.picker;
38605     /**
38606      * @event select
38607      * @param {DatePicker} picker
38608      * @param {Date} date
38609      */
38610     this.relayEvents(di, ["select"]);
38611     this.on('beforeshow', function(){
38612         if(this.picker){
38613             this.picker.hideMonthPicker(false);
38614         }
38615     }, this);
38616 };
38617 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38618     cls:'x-date-menu'
38619 });/*
38620  * Based on:
38621  * Ext JS Library 1.1.1
38622  * Copyright(c) 2006-2007, Ext JS, LLC.
38623  *
38624  * Originally Released Under LGPL - original licence link has changed is not relivant.
38625  *
38626  * Fork - LGPL
38627  * <script type="text/javascript">
38628  */
38629  
38630
38631 /**
38632  * @class Roo.menu.ColorMenu
38633  * @extends Roo.menu.Menu
38634  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38635  * @constructor
38636  * Creates a new ColorMenu
38637  * @param {Object} config Configuration options
38638  */
38639 Roo.menu.ColorMenu = function(config){
38640     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38641     this.plain = true;
38642     var ci = new Roo.menu.ColorItem(config);
38643     this.add(ci);
38644     /**
38645      * The {@link Roo.ColorPalette} instance for this ColorMenu
38646      * @type ColorPalette
38647      */
38648     this.palette = ci.palette;
38649     /**
38650      * @event select
38651      * @param {ColorPalette} palette
38652      * @param {String} color
38653      */
38654     this.relayEvents(ci, ["select"]);
38655 };
38656 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38657  * Based on:
38658  * Ext JS Library 1.1.1
38659  * Copyright(c) 2006-2007, Ext JS, LLC.
38660  *
38661  * Originally Released Under LGPL - original licence link has changed is not relivant.
38662  *
38663  * Fork - LGPL
38664  * <script type="text/javascript">
38665  */
38666  
38667 /**
38668  * @class Roo.form.Field
38669  * @extends Roo.BoxComponent
38670  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38671  * @constructor
38672  * Creates a new Field
38673  * @param {Object} config Configuration options
38674  */
38675 Roo.form.Field = function(config){
38676     Roo.form.Field.superclass.constructor.call(this, config);
38677 };
38678
38679 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38680     /**
38681      * @cfg {String} fieldLabel Label to use when rendering a form.
38682      */
38683        /**
38684      * @cfg {String} qtip Mouse over tip
38685      */
38686      
38687     /**
38688      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38689      */
38690     invalidClass : "x-form-invalid",
38691     /**
38692      * @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")
38693      */
38694     invalidText : "The value in this field is invalid",
38695     /**
38696      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38697      */
38698     focusClass : "x-form-focus",
38699     /**
38700      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38701       automatic validation (defaults to "keyup").
38702      */
38703     validationEvent : "keyup",
38704     /**
38705      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38706      */
38707     validateOnBlur : true,
38708     /**
38709      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38710      */
38711     validationDelay : 250,
38712     /**
38713      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38714      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38715      */
38716     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38717     /**
38718      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38719      */
38720     fieldClass : "x-form-field",
38721     /**
38722      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38723      *<pre>
38724 Value         Description
38725 -----------   ----------------------------------------------------------------------
38726 qtip          Display a quick tip when the user hovers over the field
38727 title         Display a default browser title attribute popup
38728 under         Add a block div beneath the field containing the error text
38729 side          Add an error icon to the right of the field with a popup on hover
38730 [element id]  Add the error text directly to the innerHTML of the specified element
38731 </pre>
38732      */
38733     msgTarget : 'qtip',
38734     /**
38735      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38736      */
38737     msgFx : 'normal',
38738
38739     /**
38740      * @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.
38741      */
38742     readOnly : false,
38743
38744     /**
38745      * @cfg {Boolean} disabled True to disable the field (defaults to false).
38746      */
38747     disabled : false,
38748
38749     /**
38750      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
38751      */
38752     inputType : undefined,
38753     
38754     /**
38755      * @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).
38756          */
38757         tabIndex : undefined,
38758         
38759     // private
38760     isFormField : true,
38761
38762     // private
38763     hasFocus : false,
38764     /**
38765      * @property {Roo.Element} fieldEl
38766      * Element Containing the rendered Field (with label etc.)
38767      */
38768     /**
38769      * @cfg {Mixed} value A value to initialize this field with.
38770      */
38771     value : undefined,
38772
38773     /**
38774      * @cfg {String} name The field's HTML name attribute.
38775      */
38776     /**
38777      * @cfg {String} cls A CSS class to apply to the field's underlying element.
38778      */
38779     // private
38780     loadedValue : false,
38781      
38782      
38783         // private ??
38784         initComponent : function(){
38785         Roo.form.Field.superclass.initComponent.call(this);
38786         this.addEvents({
38787             /**
38788              * @event focus
38789              * Fires when this field receives input focus.
38790              * @param {Roo.form.Field} this
38791              */
38792             focus : true,
38793             /**
38794              * @event blur
38795              * Fires when this field loses input focus.
38796              * @param {Roo.form.Field} this
38797              */
38798             blur : true,
38799             /**
38800              * @event specialkey
38801              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
38802              * {@link Roo.EventObject#getKey} to determine which key was pressed.
38803              * @param {Roo.form.Field} this
38804              * @param {Roo.EventObject} e The event object
38805              */
38806             specialkey : true,
38807             /**
38808              * @event change
38809              * Fires just before the field blurs if the field value has changed.
38810              * @param {Roo.form.Field} this
38811              * @param {Mixed} newValue The new value
38812              * @param {Mixed} oldValue The original value
38813              */
38814             change : true,
38815             /**
38816              * @event invalid
38817              * Fires after the field has been marked as invalid.
38818              * @param {Roo.form.Field} this
38819              * @param {String} msg The validation message
38820              */
38821             invalid : true,
38822             /**
38823              * @event valid
38824              * Fires after the field has been validated with no errors.
38825              * @param {Roo.form.Field} this
38826              */
38827             valid : true,
38828              /**
38829              * @event keyup
38830              * Fires after the key up
38831              * @param {Roo.form.Field} this
38832              * @param {Roo.EventObject}  e The event Object
38833              */
38834             keyup : true
38835         });
38836     },
38837
38838     /**
38839      * Returns the name attribute of the field if available
38840      * @return {String} name The field name
38841      */
38842     getName: function(){
38843          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
38844     },
38845
38846     // private
38847     onRender : function(ct, position){
38848         Roo.form.Field.superclass.onRender.call(this, ct, position);
38849         if(!this.el){
38850             var cfg = this.getAutoCreate();
38851             if(!cfg.name){
38852                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38853             }
38854             if (!cfg.name.length) {
38855                 delete cfg.name;
38856             }
38857             if(this.inputType){
38858                 cfg.type = this.inputType;
38859             }
38860             this.el = ct.createChild(cfg, position);
38861         }
38862         var type = this.el.dom.type;
38863         if(type){
38864             if(type == 'password'){
38865                 type = 'text';
38866             }
38867             this.el.addClass('x-form-'+type);
38868         }
38869         if(this.readOnly){
38870             this.el.dom.readOnly = true;
38871         }
38872         if(this.tabIndex !== undefined){
38873             this.el.dom.setAttribute('tabIndex', this.tabIndex);
38874         }
38875
38876         this.el.addClass([this.fieldClass, this.cls]);
38877         this.initValue();
38878     },
38879
38880     /**
38881      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
38882      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
38883      * @return {Roo.form.Field} this
38884      */
38885     applyTo : function(target){
38886         this.allowDomMove = false;
38887         this.el = Roo.get(target);
38888         this.render(this.el.dom.parentNode);
38889         return this;
38890     },
38891
38892     // private
38893     initValue : function(){
38894         if(this.value !== undefined){
38895             this.setValue(this.value);
38896         }else if(this.el.dom.value.length > 0){
38897             this.setValue(this.el.dom.value);
38898         }
38899     },
38900
38901     /**
38902      * Returns true if this field has been changed since it was originally loaded and is not disabled.
38903      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
38904      */
38905     isDirty : function() {
38906         if(this.disabled) {
38907             return false;
38908         }
38909         return String(this.getValue()) !== String(this.originalValue);
38910     },
38911
38912     /**
38913      * stores the current value in loadedValue
38914      */
38915     resetHasChanged : function()
38916     {
38917         this.loadedValue = String(this.getValue());
38918     },
38919     /**
38920      * checks the current value against the 'loaded' value.
38921      * Note - will return false if 'resetHasChanged' has not been called first.
38922      */
38923     hasChanged : function()
38924     {
38925         if(this.disabled || this.readOnly) {
38926             return false;
38927         }
38928         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
38929     },
38930     
38931     
38932     
38933     // private
38934     afterRender : function(){
38935         Roo.form.Field.superclass.afterRender.call(this);
38936         this.initEvents();
38937     },
38938
38939     // private
38940     fireKey : function(e){
38941         //Roo.log('field ' + e.getKey());
38942         if(e.isNavKeyPress()){
38943             this.fireEvent("specialkey", this, e);
38944         }
38945     },
38946
38947     /**
38948      * Resets the current field value to the originally loaded value and clears any validation messages
38949      */
38950     reset : function(){
38951         this.setValue(this.resetValue);
38952         this.originalValue = this.getValue();
38953         this.clearInvalid();
38954     },
38955
38956     // private
38957     initEvents : function(){
38958         // safari killled keypress - so keydown is now used..
38959         this.el.on("keydown" , this.fireKey,  this);
38960         this.el.on("focus", this.onFocus,  this);
38961         this.el.on("blur", this.onBlur,  this);
38962         this.el.relayEvent('keyup', this);
38963
38964         // reference to original value for reset
38965         this.originalValue = this.getValue();
38966         this.resetValue =  this.getValue();
38967     },
38968
38969     // private
38970     onFocus : function(){
38971         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38972             this.el.addClass(this.focusClass);
38973         }
38974         if(!this.hasFocus){
38975             this.hasFocus = true;
38976             this.startValue = this.getValue();
38977             this.fireEvent("focus", this);
38978         }
38979     },
38980
38981     beforeBlur : Roo.emptyFn,
38982
38983     // private
38984     onBlur : function(){
38985         this.beforeBlur();
38986         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38987             this.el.removeClass(this.focusClass);
38988         }
38989         this.hasFocus = false;
38990         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
38991             this.validate();
38992         }
38993         var v = this.getValue();
38994         if(String(v) !== String(this.startValue)){
38995             this.fireEvent('change', this, v, this.startValue);
38996         }
38997         this.fireEvent("blur", this);
38998     },
38999
39000     /**
39001      * Returns whether or not the field value is currently valid
39002      * @param {Boolean} preventMark True to disable marking the field invalid
39003      * @return {Boolean} True if the value is valid, else false
39004      */
39005     isValid : function(preventMark){
39006         if(this.disabled){
39007             return true;
39008         }
39009         var restore = this.preventMark;
39010         this.preventMark = preventMark === true;
39011         var v = this.validateValue(this.processValue(this.getRawValue()));
39012         this.preventMark = restore;
39013         return v;
39014     },
39015
39016     /**
39017      * Validates the field value
39018      * @return {Boolean} True if the value is valid, else false
39019      */
39020     validate : function(){
39021         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
39022             this.clearInvalid();
39023             return true;
39024         }
39025         return false;
39026     },
39027
39028     processValue : function(value){
39029         return value;
39030     },
39031
39032     // private
39033     // Subclasses should provide the validation implementation by overriding this
39034     validateValue : function(value){
39035         return true;
39036     },
39037
39038     /**
39039      * Mark this field as invalid
39040      * @param {String} msg The validation message
39041      */
39042     markInvalid : function(msg){
39043         if(!this.rendered || this.preventMark){ // not rendered
39044             return;
39045         }
39046         
39047         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39048         
39049         obj.el.addClass(this.invalidClass);
39050         msg = msg || this.invalidText;
39051         switch(this.msgTarget){
39052             case 'qtip':
39053                 obj.el.dom.qtip = msg;
39054                 obj.el.dom.qclass = 'x-form-invalid-tip';
39055                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
39056                     Roo.QuickTips.enable();
39057                 }
39058                 break;
39059             case 'title':
39060                 this.el.dom.title = msg;
39061                 break;
39062             case 'under':
39063                 if(!this.errorEl){
39064                     var elp = this.el.findParent('.x-form-element', 5, true);
39065                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
39066                     this.errorEl.setWidth(elp.getWidth(true)-20);
39067                 }
39068                 this.errorEl.update(msg);
39069                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
39070                 break;
39071             case 'side':
39072                 if(!this.errorIcon){
39073                     var elp = this.el.findParent('.x-form-element', 5, true);
39074                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
39075                 }
39076                 this.alignErrorIcon();
39077                 this.errorIcon.dom.qtip = msg;
39078                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
39079                 this.errorIcon.show();
39080                 this.on('resize', this.alignErrorIcon, this);
39081                 break;
39082             default:
39083                 var t = Roo.getDom(this.msgTarget);
39084                 t.innerHTML = msg;
39085                 t.style.display = this.msgDisplay;
39086                 break;
39087         }
39088         this.fireEvent('invalid', this, msg);
39089     },
39090
39091     // private
39092     alignErrorIcon : function(){
39093         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39094     },
39095
39096     /**
39097      * Clear any invalid styles/messages for this field
39098      */
39099     clearInvalid : function(){
39100         if(!this.rendered || this.preventMark){ // not rendered
39101             return;
39102         }
39103         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39104         
39105         obj.el.removeClass(this.invalidClass);
39106         switch(this.msgTarget){
39107             case 'qtip':
39108                 obj.el.dom.qtip = '';
39109                 break;
39110             case 'title':
39111                 this.el.dom.title = '';
39112                 break;
39113             case 'under':
39114                 if(this.errorEl){
39115                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39116                 }
39117                 break;
39118             case 'side':
39119                 if(this.errorIcon){
39120                     this.errorIcon.dom.qtip = '';
39121                     this.errorIcon.hide();
39122                     this.un('resize', this.alignErrorIcon, this);
39123                 }
39124                 break;
39125             default:
39126                 var t = Roo.getDom(this.msgTarget);
39127                 t.innerHTML = '';
39128                 t.style.display = 'none';
39129                 break;
39130         }
39131         this.fireEvent('valid', this);
39132     },
39133
39134     /**
39135      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39136      * @return {Mixed} value The field value
39137      */
39138     getRawValue : function(){
39139         var v = this.el.getValue();
39140         
39141         return v;
39142     },
39143
39144     /**
39145      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39146      * @return {Mixed} value The field value
39147      */
39148     getValue : function(){
39149         var v = this.el.getValue();
39150          
39151         return v;
39152     },
39153
39154     /**
39155      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39156      * @param {Mixed} value The value to set
39157      */
39158     setRawValue : function(v){
39159         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39160     },
39161
39162     /**
39163      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39164      * @param {Mixed} value The value to set
39165      */
39166     setValue : function(v){
39167         this.value = v;
39168         if(this.rendered){
39169             this.el.dom.value = (v === null || v === undefined ? '' : v);
39170              this.validate();
39171         }
39172     },
39173
39174     adjustSize : function(w, h){
39175         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39176         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39177         return s;
39178     },
39179
39180     adjustWidth : function(tag, w){
39181         tag = tag.toLowerCase();
39182         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39183             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39184                 if(tag == 'input'){
39185                     return w + 2;
39186                 }
39187                 if(tag == 'textarea'){
39188                     return w-2;
39189                 }
39190             }else if(Roo.isOpera){
39191                 if(tag == 'input'){
39192                     return w + 2;
39193                 }
39194                 if(tag == 'textarea'){
39195                     return w-2;
39196                 }
39197             }
39198         }
39199         return w;
39200     }
39201 });
39202
39203
39204 // anything other than normal should be considered experimental
39205 Roo.form.Field.msgFx = {
39206     normal : {
39207         show: function(msgEl, f){
39208             msgEl.setDisplayed('block');
39209         },
39210
39211         hide : function(msgEl, f){
39212             msgEl.setDisplayed(false).update('');
39213         }
39214     },
39215
39216     slide : {
39217         show: function(msgEl, f){
39218             msgEl.slideIn('t', {stopFx:true});
39219         },
39220
39221         hide : function(msgEl, f){
39222             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39223         }
39224     },
39225
39226     slideRight : {
39227         show: function(msgEl, f){
39228             msgEl.fixDisplay();
39229             msgEl.alignTo(f.el, 'tl-tr');
39230             msgEl.slideIn('l', {stopFx:true});
39231         },
39232
39233         hide : function(msgEl, f){
39234             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39235         }
39236     }
39237 };/*
39238  * Based on:
39239  * Ext JS Library 1.1.1
39240  * Copyright(c) 2006-2007, Ext JS, LLC.
39241  *
39242  * Originally Released Under LGPL - original licence link has changed is not relivant.
39243  *
39244  * Fork - LGPL
39245  * <script type="text/javascript">
39246  */
39247  
39248
39249 /**
39250  * @class Roo.form.TextField
39251  * @extends Roo.form.Field
39252  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39253  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39254  * @constructor
39255  * Creates a new TextField
39256  * @param {Object} config Configuration options
39257  */
39258 Roo.form.TextField = function(config){
39259     Roo.form.TextField.superclass.constructor.call(this, config);
39260     this.addEvents({
39261         /**
39262          * @event autosize
39263          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39264          * according to the default logic, but this event provides a hook for the developer to apply additional
39265          * logic at runtime to resize the field if needed.
39266              * @param {Roo.form.Field} this This text field
39267              * @param {Number} width The new field width
39268              */
39269         autosize : true
39270     });
39271 };
39272
39273 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39274     /**
39275      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39276      */
39277     grow : false,
39278     /**
39279      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39280      */
39281     growMin : 30,
39282     /**
39283      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39284      */
39285     growMax : 800,
39286     /**
39287      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39288      */
39289     vtype : null,
39290     /**
39291      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39292      */
39293     maskRe : null,
39294     /**
39295      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39296      */
39297     disableKeyFilter : false,
39298     /**
39299      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39300      */
39301     allowBlank : true,
39302     /**
39303      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39304      */
39305     minLength : 0,
39306     /**
39307      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39308      */
39309     maxLength : Number.MAX_VALUE,
39310     /**
39311      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39312      */
39313     minLengthText : "The minimum length for this field is {0}",
39314     /**
39315      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39316      */
39317     maxLengthText : "The maximum length for this field is {0}",
39318     /**
39319      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39320      */
39321     selectOnFocus : false,
39322     /**
39323      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39324      */
39325     blankText : "This field is required",
39326     /**
39327      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39328      * If available, this function will be called only after the basic validators all return true, and will be passed the
39329      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39330      */
39331     validator : null,
39332     /**
39333      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39334      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39335      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39336      */
39337     regex : null,
39338     /**
39339      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39340      */
39341     regexText : "",
39342     /**
39343      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39344      */
39345     emptyText : null,
39346    
39347
39348     // private
39349     initEvents : function()
39350     {
39351         if (this.emptyText) {
39352             this.el.attr('placeholder', this.emptyText);
39353         }
39354         
39355         Roo.form.TextField.superclass.initEvents.call(this);
39356         if(this.validationEvent == 'keyup'){
39357             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39358             this.el.on('keyup', this.filterValidation, this);
39359         }
39360         else if(this.validationEvent !== false){
39361             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39362         }
39363         
39364         if(this.selectOnFocus){
39365             this.on("focus", this.preFocus, this);
39366             
39367         }
39368         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39369             this.el.on("keypress", this.filterKeys, this);
39370         }
39371         if(this.grow){
39372             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39373             this.el.on("click", this.autoSize,  this);
39374         }
39375         if(this.el.is('input[type=password]') && Roo.isSafari){
39376             this.el.on('keydown', this.SafariOnKeyDown, this);
39377         }
39378     },
39379
39380     processValue : function(value){
39381         if(this.stripCharsRe){
39382             var newValue = value.replace(this.stripCharsRe, '');
39383             if(newValue !== value){
39384                 this.setRawValue(newValue);
39385                 return newValue;
39386             }
39387         }
39388         return value;
39389     },
39390
39391     filterValidation : function(e){
39392         if(!e.isNavKeyPress()){
39393             this.validationTask.delay(this.validationDelay);
39394         }
39395     },
39396
39397     // private
39398     onKeyUp : function(e){
39399         if(!e.isNavKeyPress()){
39400             this.autoSize();
39401         }
39402     },
39403
39404     /**
39405      * Resets the current field value to the originally-loaded value and clears any validation messages.
39406      *  
39407      */
39408     reset : function(){
39409         Roo.form.TextField.superclass.reset.call(this);
39410        
39411     },
39412
39413     
39414     // private
39415     preFocus : function(){
39416         
39417         if(this.selectOnFocus){
39418             this.el.dom.select();
39419         }
39420     },
39421
39422     
39423     // private
39424     filterKeys : function(e){
39425         var k = e.getKey();
39426         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39427             return;
39428         }
39429         var c = e.getCharCode(), cc = String.fromCharCode(c);
39430         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39431             return;
39432         }
39433         if(!this.maskRe.test(cc)){
39434             e.stopEvent();
39435         }
39436     },
39437
39438     setValue : function(v){
39439         
39440         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39441         
39442         this.autoSize();
39443     },
39444
39445     /**
39446      * Validates a value according to the field's validation rules and marks the field as invalid
39447      * if the validation fails
39448      * @param {Mixed} value The value to validate
39449      * @return {Boolean} True if the value is valid, else false
39450      */
39451     validateValue : function(value){
39452         if(value.length < 1)  { // if it's blank
39453              if(this.allowBlank){
39454                 this.clearInvalid();
39455                 return true;
39456              }else{
39457                 this.markInvalid(this.blankText);
39458                 return false;
39459              }
39460         }
39461         if(value.length < this.minLength){
39462             this.markInvalid(String.format(this.minLengthText, this.minLength));
39463             return false;
39464         }
39465         if(value.length > this.maxLength){
39466             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39467             return false;
39468         }
39469         if(this.vtype){
39470             var vt = Roo.form.VTypes;
39471             if(!vt[this.vtype](value, this)){
39472                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39473                 return false;
39474             }
39475         }
39476         if(typeof this.validator == "function"){
39477             var msg = this.validator(value);
39478             if(msg !== true){
39479                 this.markInvalid(msg);
39480                 return false;
39481             }
39482         }
39483         if(this.regex && !this.regex.test(value)){
39484             this.markInvalid(this.regexText);
39485             return false;
39486         }
39487         return true;
39488     },
39489
39490     /**
39491      * Selects text in this field
39492      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39493      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39494      */
39495     selectText : function(start, end){
39496         var v = this.getRawValue();
39497         if(v.length > 0){
39498             start = start === undefined ? 0 : start;
39499             end = end === undefined ? v.length : end;
39500             var d = this.el.dom;
39501             if(d.setSelectionRange){
39502                 d.setSelectionRange(start, end);
39503             }else if(d.createTextRange){
39504                 var range = d.createTextRange();
39505                 range.moveStart("character", start);
39506                 range.moveEnd("character", v.length-end);
39507                 range.select();
39508             }
39509         }
39510     },
39511
39512     /**
39513      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39514      * This only takes effect if grow = true, and fires the autosize event.
39515      */
39516     autoSize : function(){
39517         if(!this.grow || !this.rendered){
39518             return;
39519         }
39520         if(!this.metrics){
39521             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39522         }
39523         var el = this.el;
39524         var v = el.dom.value;
39525         var d = document.createElement('div');
39526         d.appendChild(document.createTextNode(v));
39527         v = d.innerHTML;
39528         d = null;
39529         v += "&#160;";
39530         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39531         this.el.setWidth(w);
39532         this.fireEvent("autosize", this, w);
39533     },
39534     
39535     // private
39536     SafariOnKeyDown : function(event)
39537     {
39538         // this is a workaround for a password hang bug on chrome/ webkit.
39539         
39540         var isSelectAll = false;
39541         
39542         if(this.el.dom.selectionEnd > 0){
39543             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39544         }
39545         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39546             event.preventDefault();
39547             this.setValue('');
39548             return;
39549         }
39550         
39551         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39552             
39553             event.preventDefault();
39554             // this is very hacky as keydown always get's upper case.
39555             
39556             var cc = String.fromCharCode(event.getCharCode());
39557             
39558             
39559             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39560             
39561         }
39562         
39563         
39564     }
39565 });/*
39566  * Based on:
39567  * Ext JS Library 1.1.1
39568  * Copyright(c) 2006-2007, Ext JS, LLC.
39569  *
39570  * Originally Released Under LGPL - original licence link has changed is not relivant.
39571  *
39572  * Fork - LGPL
39573  * <script type="text/javascript">
39574  */
39575  
39576 /**
39577  * @class Roo.form.Hidden
39578  * @extends Roo.form.TextField
39579  * Simple Hidden element used on forms 
39580  * 
39581  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39582  * 
39583  * @constructor
39584  * Creates a new Hidden form element.
39585  * @param {Object} config Configuration options
39586  */
39587
39588
39589
39590 // easy hidden field...
39591 Roo.form.Hidden = function(config){
39592     Roo.form.Hidden.superclass.constructor.call(this, config);
39593 };
39594   
39595 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39596     fieldLabel:      '',
39597     inputType:      'hidden',
39598     width:          50,
39599     allowBlank:     true,
39600     labelSeparator: '',
39601     hidden:         true,
39602     itemCls :       'x-form-item-display-none'
39603
39604
39605 });
39606
39607
39608 /*
39609  * Based on:
39610  * Ext JS Library 1.1.1
39611  * Copyright(c) 2006-2007, Ext JS, LLC.
39612  *
39613  * Originally Released Under LGPL - original licence link has changed is not relivant.
39614  *
39615  * Fork - LGPL
39616  * <script type="text/javascript">
39617  */
39618  
39619 /**
39620  * @class Roo.form.TriggerField
39621  * @extends Roo.form.TextField
39622  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39623  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39624  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39625  * for which you can provide a custom implementation.  For example:
39626  * <pre><code>
39627 var trigger = new Roo.form.TriggerField();
39628 trigger.onTriggerClick = myTriggerFn;
39629 trigger.applyTo('my-field');
39630 </code></pre>
39631  *
39632  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39633  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39634  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39635  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39636  * @constructor
39637  * Create a new TriggerField.
39638  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39639  * to the base TextField)
39640  */
39641 Roo.form.TriggerField = function(config){
39642     this.mimicing = false;
39643     Roo.form.TriggerField.superclass.constructor.call(this, config);
39644 };
39645
39646 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39647     /**
39648      * @cfg {String} triggerClass A CSS class to apply to the trigger
39649      */
39650     /**
39651      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39652      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39653      */
39654     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39655     /**
39656      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39657      */
39658     hideTrigger:false,
39659
39660     /** @cfg {Boolean} grow @hide */
39661     /** @cfg {Number} growMin @hide */
39662     /** @cfg {Number} growMax @hide */
39663
39664     /**
39665      * @hide 
39666      * @method
39667      */
39668     autoSize: Roo.emptyFn,
39669     // private
39670     monitorTab : true,
39671     // private
39672     deferHeight : true,
39673
39674     
39675     actionMode : 'wrap',
39676     // private
39677     onResize : function(w, h){
39678         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39679         if(typeof w == 'number'){
39680             var x = w - this.trigger.getWidth();
39681             this.el.setWidth(this.adjustWidth('input', x));
39682             this.trigger.setStyle('left', x+'px');
39683         }
39684     },
39685
39686     // private
39687     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39688
39689     // private
39690     getResizeEl : function(){
39691         return this.wrap;
39692     },
39693
39694     // private
39695     getPositionEl : function(){
39696         return this.wrap;
39697     },
39698
39699     // private
39700     alignErrorIcon : function(){
39701         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39702     },
39703
39704     // private
39705     onRender : function(ct, position){
39706         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39707         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39708         this.trigger = this.wrap.createChild(this.triggerConfig ||
39709                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39710         if(this.hideTrigger){
39711             this.trigger.setDisplayed(false);
39712         }
39713         this.initTrigger();
39714         if(!this.width){
39715             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39716         }
39717     },
39718
39719     // private
39720     initTrigger : function(){
39721         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39722         this.trigger.addClassOnOver('x-form-trigger-over');
39723         this.trigger.addClassOnClick('x-form-trigger-click');
39724     },
39725
39726     // private
39727     onDestroy : function(){
39728         if(this.trigger){
39729             this.trigger.removeAllListeners();
39730             this.trigger.remove();
39731         }
39732         if(this.wrap){
39733             this.wrap.remove();
39734         }
39735         Roo.form.TriggerField.superclass.onDestroy.call(this);
39736     },
39737
39738     // private
39739     onFocus : function(){
39740         Roo.form.TriggerField.superclass.onFocus.call(this);
39741         if(!this.mimicing){
39742             this.wrap.addClass('x-trigger-wrap-focus');
39743             this.mimicing = true;
39744             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
39745             if(this.monitorTab){
39746                 this.el.on("keydown", this.checkTab, this);
39747             }
39748         }
39749     },
39750
39751     // private
39752     checkTab : function(e){
39753         if(e.getKey() == e.TAB){
39754             this.triggerBlur();
39755         }
39756     },
39757
39758     // private
39759     onBlur : function(){
39760         // do nothing
39761     },
39762
39763     // private
39764     mimicBlur : function(e, t){
39765         if(!this.wrap.contains(t) && this.validateBlur()){
39766             this.triggerBlur();
39767         }
39768     },
39769
39770     // private
39771     triggerBlur : function(){
39772         this.mimicing = false;
39773         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
39774         if(this.monitorTab){
39775             this.el.un("keydown", this.checkTab, this);
39776         }
39777         this.wrap.removeClass('x-trigger-wrap-focus');
39778         Roo.form.TriggerField.superclass.onBlur.call(this);
39779     },
39780
39781     // private
39782     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
39783     validateBlur : function(e, t){
39784         return true;
39785     },
39786
39787     // private
39788     onDisable : function(){
39789         Roo.form.TriggerField.superclass.onDisable.call(this);
39790         if(this.wrap){
39791             this.wrap.addClass('x-item-disabled');
39792         }
39793     },
39794
39795     // private
39796     onEnable : function(){
39797         Roo.form.TriggerField.superclass.onEnable.call(this);
39798         if(this.wrap){
39799             this.wrap.removeClass('x-item-disabled');
39800         }
39801     },
39802
39803     // private
39804     onShow : function(){
39805         var ae = this.getActionEl();
39806         
39807         if(ae){
39808             ae.dom.style.display = '';
39809             ae.dom.style.visibility = 'visible';
39810         }
39811     },
39812
39813     // private
39814     
39815     onHide : function(){
39816         var ae = this.getActionEl();
39817         ae.dom.style.display = 'none';
39818     },
39819
39820     /**
39821      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
39822      * by an implementing function.
39823      * @method
39824      * @param {EventObject} e
39825      */
39826     onTriggerClick : Roo.emptyFn
39827 });
39828
39829 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
39830 // to be extended by an implementing class.  For an example of implementing this class, see the custom
39831 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
39832 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
39833     initComponent : function(){
39834         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
39835
39836         this.triggerConfig = {
39837             tag:'span', cls:'x-form-twin-triggers', cn:[
39838             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
39839             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
39840         ]};
39841     },
39842
39843     getTrigger : function(index){
39844         return this.triggers[index];
39845     },
39846
39847     initTrigger : function(){
39848         var ts = this.trigger.select('.x-form-trigger', true);
39849         this.wrap.setStyle('overflow', 'hidden');
39850         var triggerField = this;
39851         ts.each(function(t, all, index){
39852             t.hide = function(){
39853                 var w = triggerField.wrap.getWidth();
39854                 this.dom.style.display = 'none';
39855                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39856             };
39857             t.show = function(){
39858                 var w = triggerField.wrap.getWidth();
39859                 this.dom.style.display = '';
39860                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39861             };
39862             var triggerIndex = 'Trigger'+(index+1);
39863
39864             if(this['hide'+triggerIndex]){
39865                 t.dom.style.display = 'none';
39866             }
39867             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
39868             t.addClassOnOver('x-form-trigger-over');
39869             t.addClassOnClick('x-form-trigger-click');
39870         }, this);
39871         this.triggers = ts.elements;
39872     },
39873
39874     onTrigger1Click : Roo.emptyFn,
39875     onTrigger2Click : Roo.emptyFn
39876 });/*
39877  * Based on:
39878  * Ext JS Library 1.1.1
39879  * Copyright(c) 2006-2007, Ext JS, LLC.
39880  *
39881  * Originally Released Under LGPL - original licence link has changed is not relivant.
39882  *
39883  * Fork - LGPL
39884  * <script type="text/javascript">
39885  */
39886  
39887 /**
39888  * @class Roo.form.TextArea
39889  * @extends Roo.form.TextField
39890  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
39891  * support for auto-sizing.
39892  * @constructor
39893  * Creates a new TextArea
39894  * @param {Object} config Configuration options
39895  */
39896 Roo.form.TextArea = function(config){
39897     Roo.form.TextArea.superclass.constructor.call(this, config);
39898     // these are provided exchanges for backwards compat
39899     // minHeight/maxHeight were replaced by growMin/growMax to be
39900     // compatible with TextField growing config values
39901     if(this.minHeight !== undefined){
39902         this.growMin = this.minHeight;
39903     }
39904     if(this.maxHeight !== undefined){
39905         this.growMax = this.maxHeight;
39906     }
39907 };
39908
39909 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
39910     /**
39911      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
39912      */
39913     growMin : 60,
39914     /**
39915      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
39916      */
39917     growMax: 1000,
39918     /**
39919      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
39920      * in the field (equivalent to setting overflow: hidden, defaults to false)
39921      */
39922     preventScrollbars: false,
39923     /**
39924      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39925      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
39926      */
39927
39928     // private
39929     onRender : function(ct, position){
39930         if(!this.el){
39931             this.defaultAutoCreate = {
39932                 tag: "textarea",
39933                 style:"width:300px;height:60px;",
39934                 autocomplete: "new-password"
39935             };
39936         }
39937         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
39938         if(this.grow){
39939             this.textSizeEl = Roo.DomHelper.append(document.body, {
39940                 tag: "pre", cls: "x-form-grow-sizer"
39941             });
39942             if(this.preventScrollbars){
39943                 this.el.setStyle("overflow", "hidden");
39944             }
39945             this.el.setHeight(this.growMin);
39946         }
39947     },
39948
39949     onDestroy : function(){
39950         if(this.textSizeEl){
39951             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
39952         }
39953         Roo.form.TextArea.superclass.onDestroy.call(this);
39954     },
39955
39956     // private
39957     onKeyUp : function(e){
39958         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
39959             this.autoSize();
39960         }
39961     },
39962
39963     /**
39964      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
39965      * This only takes effect if grow = true, and fires the autosize event if the height changes.
39966      */
39967     autoSize : function(){
39968         if(!this.grow || !this.textSizeEl){
39969             return;
39970         }
39971         var el = this.el;
39972         var v = el.dom.value;
39973         var ts = this.textSizeEl;
39974
39975         ts.innerHTML = '';
39976         ts.appendChild(document.createTextNode(v));
39977         v = ts.innerHTML;
39978
39979         Roo.fly(ts).setWidth(this.el.getWidth());
39980         if(v.length < 1){
39981             v = "&#160;&#160;";
39982         }else{
39983             if(Roo.isIE){
39984                 v = v.replace(/\n/g, '<p>&#160;</p>');
39985             }
39986             v += "&#160;\n&#160;";
39987         }
39988         ts.innerHTML = v;
39989         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
39990         if(h != this.lastHeight){
39991             this.lastHeight = h;
39992             this.el.setHeight(h);
39993             this.fireEvent("autosize", this, h);
39994         }
39995     }
39996 });/*
39997  * Based on:
39998  * Ext JS Library 1.1.1
39999  * Copyright(c) 2006-2007, Ext JS, LLC.
40000  *
40001  * Originally Released Under LGPL - original licence link has changed is not relivant.
40002  *
40003  * Fork - LGPL
40004  * <script type="text/javascript">
40005  */
40006  
40007
40008 /**
40009  * @class Roo.form.NumberField
40010  * @extends Roo.form.TextField
40011  * Numeric text field that provides automatic keystroke filtering and numeric validation.
40012  * @constructor
40013  * Creates a new NumberField
40014  * @param {Object} config Configuration options
40015  */
40016 Roo.form.NumberField = function(config){
40017     Roo.form.NumberField.superclass.constructor.call(this, config);
40018 };
40019
40020 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
40021     /**
40022      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
40023      */
40024     fieldClass: "x-form-field x-form-num-field",
40025     /**
40026      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40027      */
40028     allowDecimals : true,
40029     /**
40030      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40031      */
40032     decimalSeparator : ".",
40033     /**
40034      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40035      */
40036     decimalPrecision : 2,
40037     /**
40038      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40039      */
40040     allowNegative : true,
40041     /**
40042      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40043      */
40044     minValue : Number.NEGATIVE_INFINITY,
40045     /**
40046      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40047      */
40048     maxValue : Number.MAX_VALUE,
40049     /**
40050      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40051      */
40052     minText : "The minimum value for this field is {0}",
40053     /**
40054      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40055      */
40056     maxText : "The maximum value for this field is {0}",
40057     /**
40058      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40059      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40060      */
40061     nanText : "{0} is not a valid number",
40062
40063     // private
40064     initEvents : function(){
40065         Roo.form.NumberField.superclass.initEvents.call(this);
40066         var allowed = "0123456789";
40067         if(this.allowDecimals){
40068             allowed += this.decimalSeparator;
40069         }
40070         if(this.allowNegative){
40071             allowed += "-";
40072         }
40073         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40074         var keyPress = function(e){
40075             var k = e.getKey();
40076             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40077                 return;
40078             }
40079             var c = e.getCharCode();
40080             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40081                 e.stopEvent();
40082             }
40083         };
40084         this.el.on("keypress", keyPress, this);
40085     },
40086
40087     // private
40088     validateValue : function(value){
40089         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40090             return false;
40091         }
40092         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40093              return true;
40094         }
40095         var num = this.parseValue(value);
40096         if(isNaN(num)){
40097             this.markInvalid(String.format(this.nanText, value));
40098             return false;
40099         }
40100         if(num < this.minValue){
40101             this.markInvalid(String.format(this.minText, this.minValue));
40102             return false;
40103         }
40104         if(num > this.maxValue){
40105             this.markInvalid(String.format(this.maxText, this.maxValue));
40106             return false;
40107         }
40108         return true;
40109     },
40110
40111     getValue : function(){
40112         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40113     },
40114
40115     // private
40116     parseValue : function(value){
40117         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40118         return isNaN(value) ? '' : value;
40119     },
40120
40121     // private
40122     fixPrecision : function(value){
40123         var nan = isNaN(value);
40124         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40125             return nan ? '' : value;
40126         }
40127         return parseFloat(value).toFixed(this.decimalPrecision);
40128     },
40129
40130     setValue : function(v){
40131         v = this.fixPrecision(v);
40132         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40133     },
40134
40135     // private
40136     decimalPrecisionFcn : function(v){
40137         return Math.floor(v);
40138     },
40139
40140     beforeBlur : function(){
40141         var v = this.parseValue(this.getRawValue());
40142         if(v){
40143             this.setValue(v);
40144         }
40145     }
40146 });/*
40147  * Based on:
40148  * Ext JS Library 1.1.1
40149  * Copyright(c) 2006-2007, Ext JS, LLC.
40150  *
40151  * Originally Released Under LGPL - original licence link has changed is not relivant.
40152  *
40153  * Fork - LGPL
40154  * <script type="text/javascript">
40155  */
40156  
40157 /**
40158  * @class Roo.form.DateField
40159  * @extends Roo.form.TriggerField
40160  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40161 * @constructor
40162 * Create a new DateField
40163 * @param {Object} config
40164  */
40165 Roo.form.DateField = function(config){
40166     Roo.form.DateField.superclass.constructor.call(this, config);
40167     
40168       this.addEvents({
40169          
40170         /**
40171          * @event select
40172          * Fires when a date is selected
40173              * @param {Roo.form.DateField} combo This combo box
40174              * @param {Date} date The date selected
40175              */
40176         'select' : true
40177          
40178     });
40179     
40180     
40181     if(typeof this.minValue == "string") {
40182         this.minValue = this.parseDate(this.minValue);
40183     }
40184     if(typeof this.maxValue == "string") {
40185         this.maxValue = this.parseDate(this.maxValue);
40186     }
40187     this.ddMatch = null;
40188     if(this.disabledDates){
40189         var dd = this.disabledDates;
40190         var re = "(?:";
40191         for(var i = 0; i < dd.length; i++){
40192             re += dd[i];
40193             if(i != dd.length-1) {
40194                 re += "|";
40195             }
40196         }
40197         this.ddMatch = new RegExp(re + ")");
40198     }
40199 };
40200
40201 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40202     /**
40203      * @cfg {String} format
40204      * The default date format string which can be overriden for localization support.  The format must be
40205      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40206      */
40207     format : "m/d/y",
40208     /**
40209      * @cfg {String} altFormats
40210      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40211      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40212      */
40213     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40214     /**
40215      * @cfg {Array} disabledDays
40216      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40217      */
40218     disabledDays : null,
40219     /**
40220      * @cfg {String} disabledDaysText
40221      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40222      */
40223     disabledDaysText : "Disabled",
40224     /**
40225      * @cfg {Array} disabledDates
40226      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40227      * expression so they are very powerful. Some examples:
40228      * <ul>
40229      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40230      * <li>["03/08", "09/16"] would disable those days for every year</li>
40231      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40232      * <li>["03/../2006"] would disable every day in March 2006</li>
40233      * <li>["^03"] would disable every day in every March</li>
40234      * </ul>
40235      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40236      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40237      */
40238     disabledDates : null,
40239     /**
40240      * @cfg {String} disabledDatesText
40241      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40242      */
40243     disabledDatesText : "Disabled",
40244     /**
40245      * @cfg {Date/String} minValue
40246      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40247      * valid format (defaults to null).
40248      */
40249     minValue : null,
40250     /**
40251      * @cfg {Date/String} maxValue
40252      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40253      * valid format (defaults to null).
40254      */
40255     maxValue : null,
40256     /**
40257      * @cfg {String} minText
40258      * The error text to display when the date in the cell is before minValue (defaults to
40259      * 'The date in this field must be after {minValue}').
40260      */
40261     minText : "The date in this field must be equal to or after {0}",
40262     /**
40263      * @cfg {String} maxText
40264      * The error text to display when the date in the cell is after maxValue (defaults to
40265      * 'The date in this field must be before {maxValue}').
40266      */
40267     maxText : "The date in this field must be equal to or before {0}",
40268     /**
40269      * @cfg {String} invalidText
40270      * The error text to display when the date in the field is invalid (defaults to
40271      * '{value} is not a valid date - it must be in the format {format}').
40272      */
40273     invalidText : "{0} is not a valid date - it must be in the format {1}",
40274     /**
40275      * @cfg {String} triggerClass
40276      * An additional CSS class used to style the trigger button.  The trigger will always get the
40277      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40278      * which displays a calendar icon).
40279      */
40280     triggerClass : 'x-form-date-trigger',
40281     
40282
40283     /**
40284      * @cfg {Boolean} useIso
40285      * if enabled, then the date field will use a hidden field to store the 
40286      * real value as iso formated date. default (false)
40287      */ 
40288     useIso : false,
40289     /**
40290      * @cfg {String/Object} autoCreate
40291      * A DomHelper element spec, or true for a default element spec (defaults to
40292      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40293      */ 
40294     // private
40295     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40296     
40297     // private
40298     hiddenField: false,
40299     
40300     onRender : function(ct, position)
40301     {
40302         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40303         if (this.useIso) {
40304             //this.el.dom.removeAttribute('name'); 
40305             Roo.log("Changing name?");
40306             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40307             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40308                     'before', true);
40309             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40310             // prevent input submission
40311             this.hiddenName = this.name;
40312         }
40313             
40314             
40315     },
40316     
40317     // private
40318     validateValue : function(value)
40319     {
40320         value = this.formatDate(value);
40321         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40322             Roo.log('super failed');
40323             return false;
40324         }
40325         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40326              return true;
40327         }
40328         var svalue = value;
40329         value = this.parseDate(value);
40330         if(!value){
40331             Roo.log('parse date failed' + svalue);
40332             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40333             return false;
40334         }
40335         var time = value.getTime();
40336         if(this.minValue && time < this.minValue.getTime()){
40337             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40338             return false;
40339         }
40340         if(this.maxValue && time > this.maxValue.getTime()){
40341             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40342             return false;
40343         }
40344         if(this.disabledDays){
40345             var day = value.getDay();
40346             for(var i = 0; i < this.disabledDays.length; i++) {
40347                 if(day === this.disabledDays[i]){
40348                     this.markInvalid(this.disabledDaysText);
40349                     return false;
40350                 }
40351             }
40352         }
40353         var fvalue = this.formatDate(value);
40354         if(this.ddMatch && this.ddMatch.test(fvalue)){
40355             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40356             return false;
40357         }
40358         return true;
40359     },
40360
40361     // private
40362     // Provides logic to override the default TriggerField.validateBlur which just returns true
40363     validateBlur : function(){
40364         return !this.menu || !this.menu.isVisible();
40365     },
40366     
40367     getName: function()
40368     {
40369         // returns hidden if it's set..
40370         if (!this.rendered) {return ''};
40371         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40372         
40373     },
40374
40375     /**
40376      * Returns the current date value of the date field.
40377      * @return {Date} The date value
40378      */
40379     getValue : function(){
40380         
40381         return  this.hiddenField ?
40382                 this.hiddenField.value :
40383                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40384     },
40385
40386     /**
40387      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40388      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40389      * (the default format used is "m/d/y").
40390      * <br />Usage:
40391      * <pre><code>
40392 //All of these calls set the same date value (May 4, 2006)
40393
40394 //Pass a date object:
40395 var dt = new Date('5/4/06');
40396 dateField.setValue(dt);
40397
40398 //Pass a date string (default format):
40399 dateField.setValue('5/4/06');
40400
40401 //Pass a date string (custom format):
40402 dateField.format = 'Y-m-d';
40403 dateField.setValue('2006-5-4');
40404 </code></pre>
40405      * @param {String/Date} date The date or valid date string
40406      */
40407     setValue : function(date){
40408         if (this.hiddenField) {
40409             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40410         }
40411         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40412         // make sure the value field is always stored as a date..
40413         this.value = this.parseDate(date);
40414         
40415         
40416     },
40417
40418     // private
40419     parseDate : function(value){
40420         if(!value || value instanceof Date){
40421             return value;
40422         }
40423         var v = Date.parseDate(value, this.format);
40424          if (!v && this.useIso) {
40425             v = Date.parseDate(value, 'Y-m-d');
40426         }
40427         if(!v && this.altFormats){
40428             if(!this.altFormatsArray){
40429                 this.altFormatsArray = this.altFormats.split("|");
40430             }
40431             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40432                 v = Date.parseDate(value, this.altFormatsArray[i]);
40433             }
40434         }
40435         return v;
40436     },
40437
40438     // private
40439     formatDate : function(date, fmt){
40440         return (!date || !(date instanceof Date)) ?
40441                date : date.dateFormat(fmt || this.format);
40442     },
40443
40444     // private
40445     menuListeners : {
40446         select: function(m, d){
40447             
40448             this.setValue(d);
40449             this.fireEvent('select', this, d);
40450         },
40451         show : function(){ // retain focus styling
40452             this.onFocus();
40453         },
40454         hide : function(){
40455             this.focus.defer(10, this);
40456             var ml = this.menuListeners;
40457             this.menu.un("select", ml.select,  this);
40458             this.menu.un("show", ml.show,  this);
40459             this.menu.un("hide", ml.hide,  this);
40460         }
40461     },
40462
40463     // private
40464     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40465     onTriggerClick : function(){
40466         if(this.disabled){
40467             return;
40468         }
40469         if(this.menu == null){
40470             this.menu = new Roo.menu.DateMenu();
40471         }
40472         Roo.apply(this.menu.picker,  {
40473             showClear: this.allowBlank,
40474             minDate : this.minValue,
40475             maxDate : this.maxValue,
40476             disabledDatesRE : this.ddMatch,
40477             disabledDatesText : this.disabledDatesText,
40478             disabledDays : this.disabledDays,
40479             disabledDaysText : this.disabledDaysText,
40480             format : this.useIso ? 'Y-m-d' : this.format,
40481             minText : String.format(this.minText, this.formatDate(this.minValue)),
40482             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40483         });
40484         this.menu.on(Roo.apply({}, this.menuListeners, {
40485             scope:this
40486         }));
40487         this.menu.picker.setValue(this.getValue() || new Date());
40488         this.menu.show(this.el, "tl-bl?");
40489     },
40490
40491     beforeBlur : function(){
40492         var v = this.parseDate(this.getRawValue());
40493         if(v){
40494             this.setValue(v);
40495         }
40496     },
40497
40498     /*@
40499      * overide
40500      * 
40501      */
40502     isDirty : function() {
40503         if(this.disabled) {
40504             return false;
40505         }
40506         
40507         if(typeof(this.startValue) === 'undefined'){
40508             return false;
40509         }
40510         
40511         return String(this.getValue()) !== String(this.startValue);
40512         
40513     }
40514 });/*
40515  * Based on:
40516  * Ext JS Library 1.1.1
40517  * Copyright(c) 2006-2007, Ext JS, LLC.
40518  *
40519  * Originally Released Under LGPL - original licence link has changed is not relivant.
40520  *
40521  * Fork - LGPL
40522  * <script type="text/javascript">
40523  */
40524  
40525 /**
40526  * @class Roo.form.MonthField
40527  * @extends Roo.form.TriggerField
40528  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40529 * @constructor
40530 * Create a new MonthField
40531 * @param {Object} config
40532  */
40533 Roo.form.MonthField = function(config){
40534     
40535     Roo.form.MonthField.superclass.constructor.call(this, config);
40536     
40537       this.addEvents({
40538          
40539         /**
40540          * @event select
40541          * Fires when a date is selected
40542              * @param {Roo.form.MonthFieeld} combo This combo box
40543              * @param {Date} date The date selected
40544              */
40545         'select' : true
40546          
40547     });
40548     
40549     
40550     if(typeof this.minValue == "string") {
40551         this.minValue = this.parseDate(this.minValue);
40552     }
40553     if(typeof this.maxValue == "string") {
40554         this.maxValue = this.parseDate(this.maxValue);
40555     }
40556     this.ddMatch = null;
40557     if(this.disabledDates){
40558         var dd = this.disabledDates;
40559         var re = "(?:";
40560         for(var i = 0; i < dd.length; i++){
40561             re += dd[i];
40562             if(i != dd.length-1) {
40563                 re += "|";
40564             }
40565         }
40566         this.ddMatch = new RegExp(re + ")");
40567     }
40568 };
40569
40570 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40571     /**
40572      * @cfg {String} format
40573      * The default date format string which can be overriden for localization support.  The format must be
40574      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40575      */
40576     format : "M Y",
40577     /**
40578      * @cfg {String} altFormats
40579      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40580      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40581      */
40582     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40583     /**
40584      * @cfg {Array} disabledDays
40585      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40586      */
40587     disabledDays : [0,1,2,3,4,5,6],
40588     /**
40589      * @cfg {String} disabledDaysText
40590      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40591      */
40592     disabledDaysText : "Disabled",
40593     /**
40594      * @cfg {Array} disabledDates
40595      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40596      * expression so they are very powerful. Some examples:
40597      * <ul>
40598      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40599      * <li>["03/08", "09/16"] would disable those days for every year</li>
40600      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40601      * <li>["03/../2006"] would disable every day in March 2006</li>
40602      * <li>["^03"] would disable every day in every March</li>
40603      * </ul>
40604      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40605      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40606      */
40607     disabledDates : null,
40608     /**
40609      * @cfg {String} disabledDatesText
40610      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40611      */
40612     disabledDatesText : "Disabled",
40613     /**
40614      * @cfg {Date/String} minValue
40615      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40616      * valid format (defaults to null).
40617      */
40618     minValue : null,
40619     /**
40620      * @cfg {Date/String} maxValue
40621      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40622      * valid format (defaults to null).
40623      */
40624     maxValue : null,
40625     /**
40626      * @cfg {String} minText
40627      * The error text to display when the date in the cell is before minValue (defaults to
40628      * 'The date in this field must be after {minValue}').
40629      */
40630     minText : "The date in this field must be equal to or after {0}",
40631     /**
40632      * @cfg {String} maxTextf
40633      * The error text to display when the date in the cell is after maxValue (defaults to
40634      * 'The date in this field must be before {maxValue}').
40635      */
40636     maxText : "The date in this field must be equal to or before {0}",
40637     /**
40638      * @cfg {String} invalidText
40639      * The error text to display when the date in the field is invalid (defaults to
40640      * '{value} is not a valid date - it must be in the format {format}').
40641      */
40642     invalidText : "{0} is not a valid date - it must be in the format {1}",
40643     /**
40644      * @cfg {String} triggerClass
40645      * An additional CSS class used to style the trigger button.  The trigger will always get the
40646      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40647      * which displays a calendar icon).
40648      */
40649     triggerClass : 'x-form-date-trigger',
40650     
40651
40652     /**
40653      * @cfg {Boolean} useIso
40654      * if enabled, then the date field will use a hidden field to store the 
40655      * real value as iso formated date. default (true)
40656      */ 
40657     useIso : true,
40658     /**
40659      * @cfg {String/Object} autoCreate
40660      * A DomHelper element spec, or true for a default element spec (defaults to
40661      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40662      */ 
40663     // private
40664     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40665     
40666     // private
40667     hiddenField: false,
40668     
40669     hideMonthPicker : false,
40670     
40671     onRender : function(ct, position)
40672     {
40673         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40674         if (this.useIso) {
40675             this.el.dom.removeAttribute('name'); 
40676             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40677                     'before', true);
40678             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40679             // prevent input submission
40680             this.hiddenName = this.name;
40681         }
40682             
40683             
40684     },
40685     
40686     // private
40687     validateValue : function(value)
40688     {
40689         value = this.formatDate(value);
40690         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40691             return false;
40692         }
40693         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40694              return true;
40695         }
40696         var svalue = value;
40697         value = this.parseDate(value);
40698         if(!value){
40699             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40700             return false;
40701         }
40702         var time = value.getTime();
40703         if(this.minValue && time < this.minValue.getTime()){
40704             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40705             return false;
40706         }
40707         if(this.maxValue && time > this.maxValue.getTime()){
40708             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40709             return false;
40710         }
40711         /*if(this.disabledDays){
40712             var day = value.getDay();
40713             for(var i = 0; i < this.disabledDays.length; i++) {
40714                 if(day === this.disabledDays[i]){
40715                     this.markInvalid(this.disabledDaysText);
40716                     return false;
40717                 }
40718             }
40719         }
40720         */
40721         var fvalue = this.formatDate(value);
40722         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
40723             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40724             return false;
40725         }
40726         */
40727         return true;
40728     },
40729
40730     // private
40731     // Provides logic to override the default TriggerField.validateBlur which just returns true
40732     validateBlur : function(){
40733         return !this.menu || !this.menu.isVisible();
40734     },
40735
40736     /**
40737      * Returns the current date value of the date field.
40738      * @return {Date} The date value
40739      */
40740     getValue : function(){
40741         
40742         
40743         
40744         return  this.hiddenField ?
40745                 this.hiddenField.value :
40746                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
40747     },
40748
40749     /**
40750      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40751      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
40752      * (the default format used is "m/d/y").
40753      * <br />Usage:
40754      * <pre><code>
40755 //All of these calls set the same date value (May 4, 2006)
40756
40757 //Pass a date object:
40758 var dt = new Date('5/4/06');
40759 monthField.setValue(dt);
40760
40761 //Pass a date string (default format):
40762 monthField.setValue('5/4/06');
40763
40764 //Pass a date string (custom format):
40765 monthField.format = 'Y-m-d';
40766 monthField.setValue('2006-5-4');
40767 </code></pre>
40768      * @param {String/Date} date The date or valid date string
40769      */
40770     setValue : function(date){
40771         Roo.log('month setValue' + date);
40772         // can only be first of month..
40773         
40774         var val = this.parseDate(date);
40775         
40776         if (this.hiddenField) {
40777             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40778         }
40779         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40780         this.value = this.parseDate(date);
40781     },
40782
40783     // private
40784     parseDate : function(value){
40785         if(!value || value instanceof Date){
40786             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
40787             return value;
40788         }
40789         var v = Date.parseDate(value, this.format);
40790         if (!v && this.useIso) {
40791             v = Date.parseDate(value, 'Y-m-d');
40792         }
40793         if (v) {
40794             // 
40795             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
40796         }
40797         
40798         
40799         if(!v && this.altFormats){
40800             if(!this.altFormatsArray){
40801                 this.altFormatsArray = this.altFormats.split("|");
40802             }
40803             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40804                 v = Date.parseDate(value, this.altFormatsArray[i]);
40805             }
40806         }
40807         return v;
40808     },
40809
40810     // private
40811     formatDate : function(date, fmt){
40812         return (!date || !(date instanceof Date)) ?
40813                date : date.dateFormat(fmt || this.format);
40814     },
40815
40816     // private
40817     menuListeners : {
40818         select: function(m, d){
40819             this.setValue(d);
40820             this.fireEvent('select', this, d);
40821         },
40822         show : function(){ // retain focus styling
40823             this.onFocus();
40824         },
40825         hide : function(){
40826             this.focus.defer(10, this);
40827             var ml = this.menuListeners;
40828             this.menu.un("select", ml.select,  this);
40829             this.menu.un("show", ml.show,  this);
40830             this.menu.un("hide", ml.hide,  this);
40831         }
40832     },
40833     // private
40834     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40835     onTriggerClick : function(){
40836         if(this.disabled){
40837             return;
40838         }
40839         if(this.menu == null){
40840             this.menu = new Roo.menu.DateMenu();
40841            
40842         }
40843         
40844         Roo.apply(this.menu.picker,  {
40845             
40846             showClear: this.allowBlank,
40847             minDate : this.minValue,
40848             maxDate : this.maxValue,
40849             disabledDatesRE : this.ddMatch,
40850             disabledDatesText : this.disabledDatesText,
40851             
40852             format : this.useIso ? 'Y-m-d' : this.format,
40853             minText : String.format(this.minText, this.formatDate(this.minValue)),
40854             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40855             
40856         });
40857          this.menu.on(Roo.apply({}, this.menuListeners, {
40858             scope:this
40859         }));
40860        
40861         
40862         var m = this.menu;
40863         var p = m.picker;
40864         
40865         // hide month picker get's called when we called by 'before hide';
40866         
40867         var ignorehide = true;
40868         p.hideMonthPicker  = function(disableAnim){
40869             if (ignorehide) {
40870                 return;
40871             }
40872              if(this.monthPicker){
40873                 Roo.log("hideMonthPicker called");
40874                 if(disableAnim === true){
40875                     this.monthPicker.hide();
40876                 }else{
40877                     this.monthPicker.slideOut('t', {duration:.2});
40878                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
40879                     p.fireEvent("select", this, this.value);
40880                     m.hide();
40881                 }
40882             }
40883         }
40884         
40885         Roo.log('picker set value');
40886         Roo.log(this.getValue());
40887         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
40888         m.show(this.el, 'tl-bl?');
40889         ignorehide  = false;
40890         // this will trigger hideMonthPicker..
40891         
40892         
40893         // hidden the day picker
40894         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
40895         
40896         
40897         
40898       
40899         
40900         p.showMonthPicker.defer(100, p);
40901     
40902         
40903        
40904     },
40905
40906     beforeBlur : function(){
40907         var v = this.parseDate(this.getRawValue());
40908         if(v){
40909             this.setValue(v);
40910         }
40911     }
40912
40913     /** @cfg {Boolean} grow @hide */
40914     /** @cfg {Number} growMin @hide */
40915     /** @cfg {Number} growMax @hide */
40916     /**
40917      * @hide
40918      * @method autoSize
40919      */
40920 });/*
40921  * Based on:
40922  * Ext JS Library 1.1.1
40923  * Copyright(c) 2006-2007, Ext JS, LLC.
40924  *
40925  * Originally Released Under LGPL - original licence link has changed is not relivant.
40926  *
40927  * Fork - LGPL
40928  * <script type="text/javascript">
40929  */
40930  
40931
40932 /**
40933  * @class Roo.form.ComboBox
40934  * @extends Roo.form.TriggerField
40935  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
40936  * @constructor
40937  * Create a new ComboBox.
40938  * @param {Object} config Configuration options
40939  */
40940 Roo.form.ComboBox = function(config){
40941     Roo.form.ComboBox.superclass.constructor.call(this, config);
40942     this.addEvents({
40943         /**
40944          * @event expand
40945          * Fires when the dropdown list is expanded
40946              * @param {Roo.form.ComboBox} combo This combo box
40947              */
40948         'expand' : true,
40949         /**
40950          * @event collapse
40951          * Fires when the dropdown list is collapsed
40952              * @param {Roo.form.ComboBox} combo This combo box
40953              */
40954         'collapse' : true,
40955         /**
40956          * @event beforeselect
40957          * Fires before a list item is selected. Return false to cancel the selection.
40958              * @param {Roo.form.ComboBox} combo This combo box
40959              * @param {Roo.data.Record} record The data record returned from the underlying store
40960              * @param {Number} index The index of the selected item in the dropdown list
40961              */
40962         'beforeselect' : true,
40963         /**
40964          * @event select
40965          * Fires when a list item is selected
40966              * @param {Roo.form.ComboBox} combo This combo box
40967              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
40968              * @param {Number} index The index of the selected item in the dropdown list
40969              */
40970         'select' : true,
40971         /**
40972          * @event beforequery
40973          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
40974          * The event object passed has these properties:
40975              * @param {Roo.form.ComboBox} combo This combo box
40976              * @param {String} query The query
40977              * @param {Boolean} forceAll true to force "all" query
40978              * @param {Boolean} cancel true to cancel the query
40979              * @param {Object} e The query event object
40980              */
40981         'beforequery': true,
40982          /**
40983          * @event add
40984          * Fires when the 'add' icon is pressed (add a listener to enable add button)
40985              * @param {Roo.form.ComboBox} combo This combo box
40986              */
40987         'add' : true,
40988         /**
40989          * @event edit
40990          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
40991              * @param {Roo.form.ComboBox} combo This combo box
40992              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
40993              */
40994         'edit' : true
40995         
40996         
40997     });
40998     if(this.transform){
40999         this.allowDomMove = false;
41000         var s = Roo.getDom(this.transform);
41001         if(!this.hiddenName){
41002             this.hiddenName = s.name;
41003         }
41004         if(!this.store){
41005             this.mode = 'local';
41006             var d = [], opts = s.options;
41007             for(var i = 0, len = opts.length;i < len; i++){
41008                 var o = opts[i];
41009                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
41010                 if(o.selected) {
41011                     this.value = value;
41012                 }
41013                 d.push([value, o.text]);
41014             }
41015             this.store = new Roo.data.SimpleStore({
41016                 'id': 0,
41017                 fields: ['value', 'text'],
41018                 data : d
41019             });
41020             this.valueField = 'value';
41021             this.displayField = 'text';
41022         }
41023         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
41024         if(!this.lazyRender){
41025             this.target = true;
41026             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
41027             s.parentNode.removeChild(s); // remove it
41028             this.render(this.el.parentNode);
41029         }else{
41030             s.parentNode.removeChild(s); // remove it
41031         }
41032
41033     }
41034     if (this.store) {
41035         this.store = Roo.factory(this.store, Roo.data);
41036     }
41037     
41038     this.selectedIndex = -1;
41039     if(this.mode == 'local'){
41040         if(config.queryDelay === undefined){
41041             this.queryDelay = 10;
41042         }
41043         if(config.minChars === undefined){
41044             this.minChars = 0;
41045         }
41046     }
41047 };
41048
41049 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
41050     /**
41051      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
41052      */
41053     /**
41054      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
41055      * rendering into an Roo.Editor, defaults to false)
41056      */
41057     /**
41058      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
41059      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
41060      */
41061     /**
41062      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
41063      */
41064     /**
41065      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
41066      * the dropdown list (defaults to undefined, with no header element)
41067      */
41068
41069      /**
41070      * @cfg {String/Roo.Template} tpl The template to use to render the output
41071      */
41072      
41073     // private
41074     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
41075     /**
41076      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
41077      */
41078     listWidth: undefined,
41079     /**
41080      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
41081      * mode = 'remote' or 'text' if mode = 'local')
41082      */
41083     displayField: undefined,
41084     /**
41085      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41086      * mode = 'remote' or 'value' if mode = 'local'). 
41087      * Note: use of a valueField requires the user make a selection
41088      * in order for a value to be mapped.
41089      */
41090     valueField: undefined,
41091     
41092     
41093     /**
41094      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41095      * field's data value (defaults to the underlying DOM element's name)
41096      */
41097     hiddenName: undefined,
41098     /**
41099      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41100      */
41101     listClass: '',
41102     /**
41103      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41104      */
41105     selectedClass: 'x-combo-selected',
41106     /**
41107      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41108      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41109      * which displays a downward arrow icon).
41110      */
41111     triggerClass : 'x-form-arrow-trigger',
41112     /**
41113      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41114      */
41115     shadow:'sides',
41116     /**
41117      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41118      * anchor positions (defaults to 'tl-bl')
41119      */
41120     listAlign: 'tl-bl?',
41121     /**
41122      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41123      */
41124     maxHeight: 300,
41125     /**
41126      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41127      * query specified by the allQuery config option (defaults to 'query')
41128      */
41129     triggerAction: 'query',
41130     /**
41131      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41132      * (defaults to 4, does not apply if editable = false)
41133      */
41134     minChars : 4,
41135     /**
41136      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41137      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41138      */
41139     typeAhead: false,
41140     /**
41141      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41142      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41143      */
41144     queryDelay: 500,
41145     /**
41146      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41147      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41148      */
41149     pageSize: 0,
41150     /**
41151      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41152      * when editable = true (defaults to false)
41153      */
41154     selectOnFocus:false,
41155     /**
41156      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41157      */
41158     queryParam: 'query',
41159     /**
41160      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41161      * when mode = 'remote' (defaults to 'Loading...')
41162      */
41163     loadingText: 'Loading...',
41164     /**
41165      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41166      */
41167     resizable: false,
41168     /**
41169      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41170      */
41171     handleHeight : 8,
41172     /**
41173      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41174      * traditional select (defaults to true)
41175      */
41176     editable: true,
41177     /**
41178      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41179      */
41180     allQuery: '',
41181     /**
41182      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41183      */
41184     mode: 'remote',
41185     /**
41186      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41187      * listWidth has a higher value)
41188      */
41189     minListWidth : 70,
41190     /**
41191      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41192      * allow the user to set arbitrary text into the field (defaults to false)
41193      */
41194     forceSelection:false,
41195     /**
41196      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41197      * if typeAhead = true (defaults to 250)
41198      */
41199     typeAheadDelay : 250,
41200     /**
41201      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41202      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41203      */
41204     valueNotFoundText : undefined,
41205     /**
41206      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41207      */
41208     blockFocus : false,
41209     
41210     /**
41211      * @cfg {Boolean} disableClear Disable showing of clear button.
41212      */
41213     disableClear : false,
41214     /**
41215      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41216      */
41217     alwaysQuery : false,
41218     
41219     //private
41220     addicon : false,
41221     editicon: false,
41222     
41223     // element that contains real text value.. (when hidden is used..)
41224      
41225     // private
41226     onRender : function(ct, position){
41227         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41228         if(this.hiddenName){
41229             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41230                     'before', true);
41231             this.hiddenField.value =
41232                 this.hiddenValue !== undefined ? this.hiddenValue :
41233                 this.value !== undefined ? this.value : '';
41234
41235             // prevent input submission
41236             this.el.dom.removeAttribute('name');
41237              
41238              
41239         }
41240         if(Roo.isGecko){
41241             this.el.dom.setAttribute('autocomplete', 'off');
41242         }
41243
41244         var cls = 'x-combo-list';
41245
41246         this.list = new Roo.Layer({
41247             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41248         });
41249
41250         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41251         this.list.setWidth(lw);
41252         this.list.swallowEvent('mousewheel');
41253         this.assetHeight = 0;
41254
41255         if(this.title){
41256             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41257             this.assetHeight += this.header.getHeight();
41258         }
41259
41260         this.innerList = this.list.createChild({cls:cls+'-inner'});
41261         this.innerList.on('mouseover', this.onViewOver, this);
41262         this.innerList.on('mousemove', this.onViewMove, this);
41263         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41264         
41265         if(this.allowBlank && !this.pageSize && !this.disableClear){
41266             this.footer = this.list.createChild({cls:cls+'-ft'});
41267             this.pageTb = new Roo.Toolbar(this.footer);
41268            
41269         }
41270         if(this.pageSize){
41271             this.footer = this.list.createChild({cls:cls+'-ft'});
41272             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41273                     {pageSize: this.pageSize});
41274             
41275         }
41276         
41277         if (this.pageTb && this.allowBlank && !this.disableClear) {
41278             var _this = this;
41279             this.pageTb.add(new Roo.Toolbar.Fill(), {
41280                 cls: 'x-btn-icon x-btn-clear',
41281                 text: '&#160;',
41282                 handler: function()
41283                 {
41284                     _this.collapse();
41285                     _this.clearValue();
41286                     _this.onSelect(false, -1);
41287                 }
41288             });
41289         }
41290         if (this.footer) {
41291             this.assetHeight += this.footer.getHeight();
41292         }
41293         
41294
41295         if(!this.tpl){
41296             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41297         }
41298
41299         this.view = new Roo.View(this.innerList, this.tpl, {
41300             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41301         });
41302
41303         this.view.on('click', this.onViewClick, this);
41304
41305         this.store.on('beforeload', this.onBeforeLoad, this);
41306         this.store.on('load', this.onLoad, this);
41307         this.store.on('loadexception', this.onLoadException, this);
41308
41309         if(this.resizable){
41310             this.resizer = new Roo.Resizable(this.list,  {
41311                pinned:true, handles:'se'
41312             });
41313             this.resizer.on('resize', function(r, w, h){
41314                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41315                 this.listWidth = w;
41316                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41317                 this.restrictHeight();
41318             }, this);
41319             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41320         }
41321         if(!this.editable){
41322             this.editable = true;
41323             this.setEditable(false);
41324         }  
41325         
41326         
41327         if (typeof(this.events.add.listeners) != 'undefined') {
41328             
41329             this.addicon = this.wrap.createChild(
41330                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41331        
41332             this.addicon.on('click', function(e) {
41333                 this.fireEvent('add', this);
41334             }, this);
41335         }
41336         if (typeof(this.events.edit.listeners) != 'undefined') {
41337             
41338             this.editicon = this.wrap.createChild(
41339                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41340             if (this.addicon) {
41341                 this.editicon.setStyle('margin-left', '40px');
41342             }
41343             this.editicon.on('click', function(e) {
41344                 
41345                 // we fire even  if inothing is selected..
41346                 this.fireEvent('edit', this, this.lastData );
41347                 
41348             }, this);
41349         }
41350         
41351         
41352         
41353     },
41354
41355     // private
41356     initEvents : function(){
41357         Roo.form.ComboBox.superclass.initEvents.call(this);
41358
41359         this.keyNav = new Roo.KeyNav(this.el, {
41360             "up" : function(e){
41361                 this.inKeyMode = true;
41362                 this.selectPrev();
41363             },
41364
41365             "down" : function(e){
41366                 if(!this.isExpanded()){
41367                     this.onTriggerClick();
41368                 }else{
41369                     this.inKeyMode = true;
41370                     this.selectNext();
41371                 }
41372             },
41373
41374             "enter" : function(e){
41375                 this.onViewClick();
41376                 //return true;
41377             },
41378
41379             "esc" : function(e){
41380                 this.collapse();
41381             },
41382
41383             "tab" : function(e){
41384                 this.onViewClick(false);
41385                 this.fireEvent("specialkey", this, e);
41386                 return true;
41387             },
41388
41389             scope : this,
41390
41391             doRelay : function(foo, bar, hname){
41392                 if(hname == 'down' || this.scope.isExpanded()){
41393                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41394                 }
41395                 return true;
41396             },
41397
41398             forceKeyDown: true
41399         });
41400         this.queryDelay = Math.max(this.queryDelay || 10,
41401                 this.mode == 'local' ? 10 : 250);
41402         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41403         if(this.typeAhead){
41404             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41405         }
41406         if(this.editable !== false){
41407             this.el.on("keyup", this.onKeyUp, this);
41408         }
41409         if(this.forceSelection){
41410             this.on('blur', this.doForce, this);
41411         }
41412     },
41413
41414     onDestroy : function(){
41415         if(this.view){
41416             this.view.setStore(null);
41417             this.view.el.removeAllListeners();
41418             this.view.el.remove();
41419             this.view.purgeListeners();
41420         }
41421         if(this.list){
41422             this.list.destroy();
41423         }
41424         if(this.store){
41425             this.store.un('beforeload', this.onBeforeLoad, this);
41426             this.store.un('load', this.onLoad, this);
41427             this.store.un('loadexception', this.onLoadException, this);
41428         }
41429         Roo.form.ComboBox.superclass.onDestroy.call(this);
41430     },
41431
41432     // private
41433     fireKey : function(e){
41434         if(e.isNavKeyPress() && !this.list.isVisible()){
41435             this.fireEvent("specialkey", this, e);
41436         }
41437     },
41438
41439     // private
41440     onResize: function(w, h){
41441         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41442         
41443         if(typeof w != 'number'){
41444             // we do not handle it!?!?
41445             return;
41446         }
41447         var tw = this.trigger.getWidth();
41448         tw += this.addicon ? this.addicon.getWidth() : 0;
41449         tw += this.editicon ? this.editicon.getWidth() : 0;
41450         var x = w - tw;
41451         this.el.setWidth( this.adjustWidth('input', x));
41452             
41453         this.trigger.setStyle('left', x+'px');
41454         
41455         if(this.list && this.listWidth === undefined){
41456             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41457             this.list.setWidth(lw);
41458             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41459         }
41460         
41461     
41462         
41463     },
41464
41465     /**
41466      * Allow or prevent the user from directly editing the field text.  If false is passed,
41467      * the user will only be able to select from the items defined in the dropdown list.  This method
41468      * is the runtime equivalent of setting the 'editable' config option at config time.
41469      * @param {Boolean} value True to allow the user to directly edit the field text
41470      */
41471     setEditable : function(value){
41472         if(value == this.editable){
41473             return;
41474         }
41475         this.editable = value;
41476         if(!value){
41477             this.el.dom.setAttribute('readOnly', true);
41478             this.el.on('mousedown', this.onTriggerClick,  this);
41479             this.el.addClass('x-combo-noedit');
41480         }else{
41481             this.el.dom.setAttribute('readOnly', false);
41482             this.el.un('mousedown', this.onTriggerClick,  this);
41483             this.el.removeClass('x-combo-noedit');
41484         }
41485     },
41486
41487     // private
41488     onBeforeLoad : function(){
41489         if(!this.hasFocus){
41490             return;
41491         }
41492         this.innerList.update(this.loadingText ?
41493                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41494         this.restrictHeight();
41495         this.selectedIndex = -1;
41496     },
41497
41498     // private
41499     onLoad : function(){
41500         if(!this.hasFocus){
41501             return;
41502         }
41503         if(this.store.getCount() > 0){
41504             this.expand();
41505             this.restrictHeight();
41506             if(this.lastQuery == this.allQuery){
41507                 if(this.editable){
41508                     this.el.dom.select();
41509                 }
41510                 if(!this.selectByValue(this.value, true)){
41511                     this.select(0, true);
41512                 }
41513             }else{
41514                 this.selectNext();
41515                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41516                     this.taTask.delay(this.typeAheadDelay);
41517                 }
41518             }
41519         }else{
41520             this.onEmptyResults();
41521         }
41522         //this.el.focus();
41523     },
41524     // private
41525     onLoadException : function()
41526     {
41527         this.collapse();
41528         Roo.log(this.store.reader.jsonData);
41529         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41530             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41531         }
41532         
41533         
41534     },
41535     // private
41536     onTypeAhead : function(){
41537         if(this.store.getCount() > 0){
41538             var r = this.store.getAt(0);
41539             var newValue = r.data[this.displayField];
41540             var len = newValue.length;
41541             var selStart = this.getRawValue().length;
41542             if(selStart != len){
41543                 this.setRawValue(newValue);
41544                 this.selectText(selStart, newValue.length);
41545             }
41546         }
41547     },
41548
41549     // private
41550     onSelect : function(record, index){
41551         if(this.fireEvent('beforeselect', this, record, index) !== false){
41552             this.setFromData(index > -1 ? record.data : false);
41553             this.collapse();
41554             this.fireEvent('select', this, record, index);
41555         }
41556     },
41557
41558     /**
41559      * Returns the currently selected field value or empty string if no value is set.
41560      * @return {String} value The selected value
41561      */
41562     getValue : function(){
41563         if(this.valueField){
41564             return typeof this.value != 'undefined' ? this.value : '';
41565         }
41566         return Roo.form.ComboBox.superclass.getValue.call(this);
41567     },
41568
41569     /**
41570      * Clears any text/value currently set in the field
41571      */
41572     clearValue : function(){
41573         if(this.hiddenField){
41574             this.hiddenField.value = '';
41575         }
41576         this.value = '';
41577         this.setRawValue('');
41578         this.lastSelectionText = '';
41579         
41580     },
41581
41582     /**
41583      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41584      * will be displayed in the field.  If the value does not match the data value of an existing item,
41585      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41586      * Otherwise the field will be blank (although the value will still be set).
41587      * @param {String} value The value to match
41588      */
41589     setValue : function(v){
41590         var text = v;
41591         if(this.valueField){
41592             var r = this.findRecord(this.valueField, v);
41593             if(r){
41594                 text = r.data[this.displayField];
41595             }else if(this.valueNotFoundText !== undefined){
41596                 text = this.valueNotFoundText;
41597             }
41598         }
41599         this.lastSelectionText = text;
41600         if(this.hiddenField){
41601             this.hiddenField.value = v;
41602         }
41603         Roo.form.ComboBox.superclass.setValue.call(this, text);
41604         this.value = v;
41605     },
41606     /**
41607      * @property {Object} the last set data for the element
41608      */
41609     
41610     lastData : false,
41611     /**
41612      * Sets the value of the field based on a object which is related to the record format for the store.
41613      * @param {Object} value the value to set as. or false on reset?
41614      */
41615     setFromData : function(o){
41616         var dv = ''; // display value
41617         var vv = ''; // value value..
41618         this.lastData = o;
41619         if (this.displayField) {
41620             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41621         } else {
41622             // this is an error condition!!!
41623             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41624         }
41625         
41626         if(this.valueField){
41627             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41628         }
41629         if(this.hiddenField){
41630             this.hiddenField.value = vv;
41631             
41632             this.lastSelectionText = dv;
41633             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41634             this.value = vv;
41635             return;
41636         }
41637         // no hidden field.. - we store the value in 'value', but still display
41638         // display field!!!!
41639         this.lastSelectionText = dv;
41640         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41641         this.value = vv;
41642         
41643         
41644     },
41645     // private
41646     reset : function(){
41647         // overridden so that last data is reset..
41648         this.setValue(this.resetValue);
41649         this.originalValue = this.getValue();
41650         this.clearInvalid();
41651         this.lastData = false;
41652         if (this.view) {
41653             this.view.clearSelections();
41654         }
41655     },
41656     // private
41657     findRecord : function(prop, value){
41658         var record;
41659         if(this.store.getCount() > 0){
41660             this.store.each(function(r){
41661                 if(r.data[prop] == value){
41662                     record = r;
41663                     return false;
41664                 }
41665                 return true;
41666             });
41667         }
41668         return record;
41669     },
41670     
41671     getName: function()
41672     {
41673         // returns hidden if it's set..
41674         if (!this.rendered) {return ''};
41675         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41676         
41677     },
41678     // private
41679     onViewMove : function(e, t){
41680         this.inKeyMode = false;
41681     },
41682
41683     // private
41684     onViewOver : function(e, t){
41685         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41686             return;
41687         }
41688         var item = this.view.findItemFromChild(t);
41689         if(item){
41690             var index = this.view.indexOf(item);
41691             this.select(index, false);
41692         }
41693     },
41694
41695     // private
41696     onViewClick : function(doFocus)
41697     {
41698         var index = this.view.getSelectedIndexes()[0];
41699         var r = this.store.getAt(index);
41700         if(r){
41701             this.onSelect(r, index);
41702         }
41703         if(doFocus !== false && !this.blockFocus){
41704             this.el.focus();
41705         }
41706     },
41707
41708     // private
41709     restrictHeight : function(){
41710         this.innerList.dom.style.height = '';
41711         var inner = this.innerList.dom;
41712         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41713         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41714         this.list.beginUpdate();
41715         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41716         this.list.alignTo(this.el, this.listAlign);
41717         this.list.endUpdate();
41718     },
41719
41720     // private
41721     onEmptyResults : function(){
41722         this.collapse();
41723     },
41724
41725     /**
41726      * Returns true if the dropdown list is expanded, else false.
41727      */
41728     isExpanded : function(){
41729         return this.list.isVisible();
41730     },
41731
41732     /**
41733      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
41734      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41735      * @param {String} value The data value of the item to select
41736      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41737      * selected item if it is not currently in view (defaults to true)
41738      * @return {Boolean} True if the value matched an item in the list, else false
41739      */
41740     selectByValue : function(v, scrollIntoView){
41741         if(v !== undefined && v !== null){
41742             var r = this.findRecord(this.valueField || this.displayField, v);
41743             if(r){
41744                 this.select(this.store.indexOf(r), scrollIntoView);
41745                 return true;
41746             }
41747         }
41748         return false;
41749     },
41750
41751     /**
41752      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
41753      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41754      * @param {Number} index The zero-based index of the list item to select
41755      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41756      * selected item if it is not currently in view (defaults to true)
41757      */
41758     select : function(index, scrollIntoView){
41759         this.selectedIndex = index;
41760         this.view.select(index);
41761         if(scrollIntoView !== false){
41762             var el = this.view.getNode(index);
41763             if(el){
41764                 this.innerList.scrollChildIntoView(el, false);
41765             }
41766         }
41767     },
41768
41769     // private
41770     selectNext : function(){
41771         var ct = this.store.getCount();
41772         if(ct > 0){
41773             if(this.selectedIndex == -1){
41774                 this.select(0);
41775             }else if(this.selectedIndex < ct-1){
41776                 this.select(this.selectedIndex+1);
41777             }
41778         }
41779     },
41780
41781     // private
41782     selectPrev : function(){
41783         var ct = this.store.getCount();
41784         if(ct > 0){
41785             if(this.selectedIndex == -1){
41786                 this.select(0);
41787             }else if(this.selectedIndex != 0){
41788                 this.select(this.selectedIndex-1);
41789             }
41790         }
41791     },
41792
41793     // private
41794     onKeyUp : function(e){
41795         if(this.editable !== false && !e.isSpecialKey()){
41796             this.lastKey = e.getKey();
41797             this.dqTask.delay(this.queryDelay);
41798         }
41799     },
41800
41801     // private
41802     validateBlur : function(){
41803         return !this.list || !this.list.isVisible();   
41804     },
41805
41806     // private
41807     initQuery : function(){
41808         this.doQuery(this.getRawValue());
41809     },
41810
41811     // private
41812     doForce : function(){
41813         if(this.el.dom.value.length > 0){
41814             this.el.dom.value =
41815                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
41816              
41817         }
41818     },
41819
41820     /**
41821      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
41822      * query allowing the query action to be canceled if needed.
41823      * @param {String} query The SQL query to execute
41824      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
41825      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
41826      * saved in the current store (defaults to false)
41827      */
41828     doQuery : function(q, forceAll){
41829         if(q === undefined || q === null){
41830             q = '';
41831         }
41832         var qe = {
41833             query: q,
41834             forceAll: forceAll,
41835             combo: this,
41836             cancel:false
41837         };
41838         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
41839             return false;
41840         }
41841         q = qe.query;
41842         forceAll = qe.forceAll;
41843         if(forceAll === true || (q.length >= this.minChars)){
41844             if(this.lastQuery != q || this.alwaysQuery){
41845                 this.lastQuery = q;
41846                 if(this.mode == 'local'){
41847                     this.selectedIndex = -1;
41848                     if(forceAll){
41849                         this.store.clearFilter();
41850                     }else{
41851                         this.store.filter(this.displayField, q);
41852                     }
41853                     this.onLoad();
41854                 }else{
41855                     this.store.baseParams[this.queryParam] = q;
41856                     this.store.load({
41857                         params: this.getParams(q)
41858                     });
41859                     this.expand();
41860                 }
41861             }else{
41862                 this.selectedIndex = -1;
41863                 this.onLoad();   
41864             }
41865         }
41866     },
41867
41868     // private
41869     getParams : function(q){
41870         var p = {};
41871         //p[this.queryParam] = q;
41872         if(this.pageSize){
41873             p.start = 0;
41874             p.limit = this.pageSize;
41875         }
41876         return p;
41877     },
41878
41879     /**
41880      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
41881      */
41882     collapse : function(){
41883         if(!this.isExpanded()){
41884             return;
41885         }
41886         this.list.hide();
41887         Roo.get(document).un('mousedown', this.collapseIf, this);
41888         Roo.get(document).un('mousewheel', this.collapseIf, this);
41889         if (!this.editable) {
41890             Roo.get(document).un('keydown', this.listKeyPress, this);
41891         }
41892         this.fireEvent('collapse', this);
41893     },
41894
41895     // private
41896     collapseIf : function(e){
41897         if(!e.within(this.wrap) && !e.within(this.list)){
41898             this.collapse();
41899         }
41900     },
41901
41902     /**
41903      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
41904      */
41905     expand : function(){
41906         if(this.isExpanded() || !this.hasFocus){
41907             return;
41908         }
41909         this.list.alignTo(this.el, this.listAlign);
41910         this.list.show();
41911         Roo.get(document).on('mousedown', this.collapseIf, this);
41912         Roo.get(document).on('mousewheel', this.collapseIf, this);
41913         if (!this.editable) {
41914             Roo.get(document).on('keydown', this.listKeyPress, this);
41915         }
41916         
41917         this.fireEvent('expand', this);
41918     },
41919
41920     // private
41921     // Implements the default empty TriggerField.onTriggerClick function
41922     onTriggerClick : function(){
41923         if(this.disabled){
41924             return;
41925         }
41926         if(this.isExpanded()){
41927             this.collapse();
41928             if (!this.blockFocus) {
41929                 this.el.focus();
41930             }
41931             
41932         }else {
41933             this.hasFocus = true;
41934             if(this.triggerAction == 'all') {
41935                 this.doQuery(this.allQuery, true);
41936             } else {
41937                 this.doQuery(this.getRawValue());
41938             }
41939             if (!this.blockFocus) {
41940                 this.el.focus();
41941             }
41942         }
41943     },
41944     listKeyPress : function(e)
41945     {
41946         //Roo.log('listkeypress');
41947         // scroll to first matching element based on key pres..
41948         if (e.isSpecialKey()) {
41949             return false;
41950         }
41951         var k = String.fromCharCode(e.getKey()).toUpperCase();
41952         //Roo.log(k);
41953         var match  = false;
41954         var csel = this.view.getSelectedNodes();
41955         var cselitem = false;
41956         if (csel.length) {
41957             var ix = this.view.indexOf(csel[0]);
41958             cselitem  = this.store.getAt(ix);
41959             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
41960                 cselitem = false;
41961             }
41962             
41963         }
41964         
41965         this.store.each(function(v) { 
41966             if (cselitem) {
41967                 // start at existing selection.
41968                 if (cselitem.id == v.id) {
41969                     cselitem = false;
41970                 }
41971                 return;
41972             }
41973                 
41974             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
41975                 match = this.store.indexOf(v);
41976                 return false;
41977             }
41978         }, this);
41979         
41980         if (match === false) {
41981             return true; // no more action?
41982         }
41983         // scroll to?
41984         this.view.select(match);
41985         var sn = Roo.get(this.view.getSelectedNodes()[0]);
41986         sn.scrollIntoView(sn.dom.parentNode, false);
41987     }
41988
41989     /** 
41990     * @cfg {Boolean} grow 
41991     * @hide 
41992     */
41993     /** 
41994     * @cfg {Number} growMin 
41995     * @hide 
41996     */
41997     /** 
41998     * @cfg {Number} growMax 
41999     * @hide 
42000     */
42001     /**
42002      * @hide
42003      * @method autoSize
42004      */
42005 });/*
42006  * Copyright(c) 2010-2012, Roo J Solutions Limited
42007  *
42008  * Licence LGPL
42009  *
42010  */
42011
42012 /**
42013  * @class Roo.form.ComboBoxArray
42014  * @extends Roo.form.TextField
42015  * A facebook style adder... for lists of email / people / countries  etc...
42016  * pick multiple items from a combo box, and shows each one.
42017  *
42018  *  Fred [x]  Brian [x]  [Pick another |v]
42019  *
42020  *
42021  *  For this to work: it needs various extra information
42022  *    - normal combo problay has
42023  *      name, hiddenName
42024  *    + displayField, valueField
42025  *
42026  *    For our purpose...
42027  *
42028  *
42029  *   If we change from 'extends' to wrapping...
42030  *   
42031  *  
42032  *
42033  
42034  
42035  * @constructor
42036  * Create a new ComboBoxArray.
42037  * @param {Object} config Configuration options
42038  */
42039  
42040
42041 Roo.form.ComboBoxArray = function(config)
42042 {
42043     this.addEvents({
42044         /**
42045          * @event beforeremove
42046          * Fires before remove the value from the list
42047              * @param {Roo.form.ComboBoxArray} _self This combo box array
42048              * @param {Roo.form.ComboBoxArray.Item} item removed item
42049              */
42050         'beforeremove' : true,
42051         /**
42052          * @event remove
42053          * Fires when remove the value from the list
42054              * @param {Roo.form.ComboBoxArray} _self This combo box array
42055              * @param {Roo.form.ComboBoxArray.Item} item removed item
42056              */
42057         'remove' : true
42058         
42059         
42060     });
42061     
42062     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
42063     
42064     this.items = new Roo.util.MixedCollection(false);
42065     
42066     // construct the child combo...
42067     
42068     
42069     
42070     
42071    
42072     
42073 }
42074
42075  
42076 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
42077
42078     /**
42079      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
42080      */
42081     
42082     lastData : false,
42083     
42084     // behavies liek a hiddne field
42085     inputType:      'hidden',
42086     /**
42087      * @cfg {Number} width The width of the box that displays the selected element
42088      */ 
42089     width:          300,
42090
42091     
42092     
42093     /**
42094      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42095      */
42096     name : false,
42097     /**
42098      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42099      */
42100     hiddenName : false,
42101     
42102     
42103     // private the array of items that are displayed..
42104     items  : false,
42105     // private - the hidden field el.
42106     hiddenEl : false,
42107     // private - the filed el..
42108     el : false,
42109     
42110     //validateValue : function() { return true; }, // all values are ok!
42111     //onAddClick: function() { },
42112     
42113     onRender : function(ct, position) 
42114     {
42115         
42116         // create the standard hidden element
42117         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42118         
42119         
42120         // give fake names to child combo;
42121         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42122         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
42123         
42124         this.combo = Roo.factory(this.combo, Roo.form);
42125         this.combo.onRender(ct, position);
42126         if (typeof(this.combo.width) != 'undefined') {
42127             this.combo.onResize(this.combo.width,0);
42128         }
42129         
42130         this.combo.initEvents();
42131         
42132         // assigned so form know we need to do this..
42133         this.store          = this.combo.store;
42134         this.valueField     = this.combo.valueField;
42135         this.displayField   = this.combo.displayField ;
42136         
42137         
42138         this.combo.wrap.addClass('x-cbarray-grp');
42139         
42140         var cbwrap = this.combo.wrap.createChild(
42141             {tag: 'div', cls: 'x-cbarray-cb'},
42142             this.combo.el.dom
42143         );
42144         
42145              
42146         this.hiddenEl = this.combo.wrap.createChild({
42147             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42148         });
42149         this.el = this.combo.wrap.createChild({
42150             tag: 'input',  type:'hidden' , name: this.name, value : ''
42151         });
42152          //   this.el.dom.removeAttribute("name");
42153         
42154         
42155         this.outerWrap = this.combo.wrap;
42156         this.wrap = cbwrap;
42157         
42158         this.outerWrap.setWidth(this.width);
42159         this.outerWrap.dom.removeChild(this.el.dom);
42160         
42161         this.wrap.dom.appendChild(this.el.dom);
42162         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42163         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42164         
42165         this.combo.trigger.setStyle('position','relative');
42166         this.combo.trigger.setStyle('left', '0px');
42167         this.combo.trigger.setStyle('top', '2px');
42168         
42169         this.combo.el.setStyle('vertical-align', 'text-bottom');
42170         
42171         //this.trigger.setStyle('vertical-align', 'top');
42172         
42173         // this should use the code from combo really... on('add' ....)
42174         if (this.adder) {
42175             
42176         
42177             this.adder = this.outerWrap.createChild(
42178                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42179             var _t = this;
42180             this.adder.on('click', function(e) {
42181                 _t.fireEvent('adderclick', this, e);
42182             }, _t);
42183         }
42184         //var _t = this;
42185         //this.adder.on('click', this.onAddClick, _t);
42186         
42187         
42188         this.combo.on('select', function(cb, rec, ix) {
42189             this.addItem(rec.data);
42190             
42191             cb.setValue('');
42192             cb.el.dom.value = '';
42193             //cb.lastData = rec.data;
42194             // add to list
42195             
42196         }, this);
42197         
42198         
42199     },
42200     
42201     
42202     getName: function()
42203     {
42204         // returns hidden if it's set..
42205         if (!this.rendered) {return ''};
42206         return  this.hiddenName ? this.hiddenName : this.name;
42207         
42208     },
42209     
42210     
42211     onResize: function(w, h){
42212         
42213         return;
42214         // not sure if this is needed..
42215         //this.combo.onResize(w,h);
42216         
42217         if(typeof w != 'number'){
42218             // we do not handle it!?!?
42219             return;
42220         }
42221         var tw = this.combo.trigger.getWidth();
42222         tw += this.addicon ? this.addicon.getWidth() : 0;
42223         tw += this.editicon ? this.editicon.getWidth() : 0;
42224         var x = w - tw;
42225         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42226             
42227         this.combo.trigger.setStyle('left', '0px');
42228         
42229         if(this.list && this.listWidth === undefined){
42230             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42231             this.list.setWidth(lw);
42232             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42233         }
42234         
42235     
42236         
42237     },
42238     
42239     addItem: function(rec)
42240     {
42241         var valueField = this.combo.valueField;
42242         var displayField = this.combo.displayField;
42243         if (this.items.indexOfKey(rec[valueField]) > -1) {
42244             //console.log("GOT " + rec.data.id);
42245             return;
42246         }
42247         
42248         var x = new Roo.form.ComboBoxArray.Item({
42249             //id : rec[this.idField],
42250             data : rec,
42251             displayField : displayField ,
42252             tipField : displayField ,
42253             cb : this
42254         });
42255         // use the 
42256         this.items.add(rec[valueField],x);
42257         // add it before the element..
42258         this.updateHiddenEl();
42259         x.render(this.outerWrap, this.wrap.dom);
42260         // add the image handler..
42261     },
42262     
42263     updateHiddenEl : function()
42264     {
42265         this.validate();
42266         if (!this.hiddenEl) {
42267             return;
42268         }
42269         var ar = [];
42270         var idField = this.combo.valueField;
42271         
42272         this.items.each(function(f) {
42273             ar.push(f.data[idField]);
42274            
42275         });
42276         this.hiddenEl.dom.value = ar.join(',');
42277         this.validate();
42278     },
42279     
42280     reset : function()
42281     {
42282         this.items.clear();
42283         
42284         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42285            el.remove();
42286         });
42287         
42288         this.el.dom.value = '';
42289         if (this.hiddenEl) {
42290             this.hiddenEl.dom.value = '';
42291         }
42292         
42293     },
42294     getValue: function()
42295     {
42296         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42297     },
42298     setValue: function(v) // not a valid action - must use addItems..
42299     {
42300          
42301         this.reset();
42302         
42303         
42304         
42305         if (this.store.isLocal && (typeof(v) == 'string')) {
42306             // then we can use the store to find the values..
42307             // comma seperated at present.. this needs to allow JSON based encoding..
42308             this.hiddenEl.value  = v;
42309             var v_ar = [];
42310             Roo.each(v.split(','), function(k) {
42311                 Roo.log("CHECK " + this.valueField + ',' + k);
42312                 var li = this.store.query(this.valueField, k);
42313                 if (!li.length) {
42314                     return;
42315                 }
42316                 var add = {};
42317                 add[this.valueField] = k;
42318                 add[this.displayField] = li.item(0).data[this.displayField];
42319                 
42320                 this.addItem(add);
42321             }, this) 
42322              
42323         }
42324         if (typeof(v) == 'object' ) {
42325             // then let's assume it's an array of objects..
42326             Roo.each(v, function(l) {
42327                 this.addItem(l);
42328             }, this);
42329              
42330         }
42331         
42332         
42333     },
42334     setFromData: function(v)
42335     {
42336         // this recieves an object, if setValues is called.
42337         this.reset();
42338         this.el.dom.value = v[this.displayField];
42339         this.hiddenEl.dom.value = v[this.valueField];
42340         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42341             return;
42342         }
42343         var kv = v[this.valueField];
42344         var dv = v[this.displayField];
42345         kv = typeof(kv) != 'string' ? '' : kv;
42346         dv = typeof(dv) != 'string' ? '' : dv;
42347         
42348         
42349         var keys = kv.split(',');
42350         var display = dv.split(',');
42351         for (var i = 0 ; i < keys.length; i++) {
42352             
42353             add = {};
42354             add[this.valueField] = keys[i];
42355             add[this.displayField] = display[i];
42356             this.addItem(add);
42357         }
42358       
42359         
42360     },
42361     
42362     /**
42363      * Validates the combox array value
42364      * @return {Boolean} True if the value is valid, else false
42365      */
42366     validate : function(){
42367         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42368             this.clearInvalid();
42369             return true;
42370         }
42371         return false;
42372     },
42373     
42374     validateValue : function(value){
42375         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42376         
42377     },
42378     
42379     /*@
42380      * overide
42381      * 
42382      */
42383     isDirty : function() {
42384         if(this.disabled) {
42385             return false;
42386         }
42387         
42388         try {
42389             var d = Roo.decode(String(this.originalValue));
42390         } catch (e) {
42391             return String(this.getValue()) !== String(this.originalValue);
42392         }
42393         
42394         var originalValue = [];
42395         
42396         for (var i = 0; i < d.length; i++){
42397             originalValue.push(d[i][this.valueField]);
42398         }
42399         
42400         return String(this.getValue()) !== String(originalValue.join(','));
42401         
42402     }
42403     
42404 });
42405
42406
42407
42408 /**
42409  * @class Roo.form.ComboBoxArray.Item
42410  * @extends Roo.BoxComponent
42411  * A selected item in the list
42412  *  Fred [x]  Brian [x]  [Pick another |v]
42413  * 
42414  * @constructor
42415  * Create a new item.
42416  * @param {Object} config Configuration options
42417  */
42418  
42419 Roo.form.ComboBoxArray.Item = function(config) {
42420     config.id = Roo.id();
42421     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42422 }
42423
42424 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42425     data : {},
42426     cb: false,
42427     displayField : false,
42428     tipField : false,
42429     
42430     
42431     defaultAutoCreate : {
42432         tag: 'div',
42433         cls: 'x-cbarray-item',
42434         cn : [ 
42435             { tag: 'div' },
42436             {
42437                 tag: 'img',
42438                 width:16,
42439                 height : 16,
42440                 src : Roo.BLANK_IMAGE_URL ,
42441                 align: 'center'
42442             }
42443         ]
42444         
42445     },
42446     
42447  
42448     onRender : function(ct, position)
42449     {
42450         Roo.form.Field.superclass.onRender.call(this, ct, position);
42451         
42452         if(!this.el){
42453             var cfg = this.getAutoCreate();
42454             this.el = ct.createChild(cfg, position);
42455         }
42456         
42457         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42458         
42459         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42460             this.cb.renderer(this.data) :
42461             String.format('{0}',this.data[this.displayField]);
42462         
42463             
42464         this.el.child('div').dom.setAttribute('qtip',
42465                         String.format('{0}',this.data[this.tipField])
42466         );
42467         
42468         this.el.child('img').on('click', this.remove, this);
42469         
42470     },
42471    
42472     remove : function()
42473     {
42474         if(this.cb.disabled){
42475             return;
42476         }
42477         
42478         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42479             this.cb.items.remove(this);
42480             this.el.child('img').un('click', this.remove, this);
42481             this.el.remove();
42482             this.cb.updateHiddenEl();
42483
42484             this.cb.fireEvent('remove', this.cb, this);
42485         }
42486         
42487     }
42488 });/*
42489  * Based on:
42490  * Ext JS Library 1.1.1
42491  * Copyright(c) 2006-2007, Ext JS, LLC.
42492  *
42493  * Originally Released Under LGPL - original licence link has changed is not relivant.
42494  *
42495  * Fork - LGPL
42496  * <script type="text/javascript">
42497  */
42498 /**
42499  * @class Roo.form.Checkbox
42500  * @extends Roo.form.Field
42501  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
42502  * @constructor
42503  * Creates a new Checkbox
42504  * @param {Object} config Configuration options
42505  */
42506 Roo.form.Checkbox = function(config){
42507     Roo.form.Checkbox.superclass.constructor.call(this, config);
42508     this.addEvents({
42509         /**
42510          * @event check
42511          * Fires when the checkbox is checked or unchecked.
42512              * @param {Roo.form.Checkbox} this This checkbox
42513              * @param {Boolean} checked The new checked value
42514              */
42515         check : true
42516     });
42517 };
42518
42519 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
42520     /**
42521      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42522      */
42523     focusClass : undefined,
42524     /**
42525      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42526      */
42527     fieldClass: "x-form-field",
42528     /**
42529      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
42530      */
42531     checked: false,
42532     /**
42533      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42534      * {tag: "input", type: "checkbox", autocomplete: "off"})
42535      */
42536     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
42537     /**
42538      * @cfg {String} boxLabel The text that appears beside the checkbox
42539      */
42540     boxLabel : "",
42541     /**
42542      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
42543      */  
42544     inputValue : '1',
42545     /**
42546      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
42547      */
42548      valueOff: '0', // value when not checked..
42549
42550     actionMode : 'viewEl', 
42551     //
42552     // private
42553     itemCls : 'x-menu-check-item x-form-item',
42554     groupClass : 'x-menu-group-item',
42555     inputType : 'hidden',
42556     
42557     
42558     inSetChecked: false, // check that we are not calling self...
42559     
42560     inputElement: false, // real input element?
42561     basedOn: false, // ????
42562     
42563     isFormField: true, // not sure where this is needed!!!!
42564
42565     onResize : function(){
42566         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
42567         if(!this.boxLabel){
42568             this.el.alignTo(this.wrap, 'c-c');
42569         }
42570     },
42571
42572     initEvents : function(){
42573         Roo.form.Checkbox.superclass.initEvents.call(this);
42574         this.el.on("click", this.onClick,  this);
42575         this.el.on("change", this.onClick,  this);
42576     },
42577
42578
42579     getResizeEl : function(){
42580         return this.wrap;
42581     },
42582
42583     getPositionEl : function(){
42584         return this.wrap;
42585     },
42586
42587     // private
42588     onRender : function(ct, position){
42589         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42590         /*
42591         if(this.inputValue !== undefined){
42592             this.el.dom.value = this.inputValue;
42593         }
42594         */
42595         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42596         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42597         var viewEl = this.wrap.createChild({ 
42598             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42599         this.viewEl = viewEl;   
42600         this.wrap.on('click', this.onClick,  this); 
42601         
42602         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42603         this.el.on('propertychange', this.setFromHidden,  this);  //ie
42604         
42605         
42606         
42607         if(this.boxLabel){
42608             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42609         //    viewEl.on('click', this.onClick,  this); 
42610         }
42611         //if(this.checked){
42612             this.setChecked(this.checked);
42613         //}else{
42614             //this.checked = this.el.dom;
42615         //}
42616
42617     },
42618
42619     // private
42620     initValue : Roo.emptyFn,
42621
42622     /**
42623      * Returns the checked state of the checkbox.
42624      * @return {Boolean} True if checked, else false
42625      */
42626     getValue : function(){
42627         if(this.el){
42628             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
42629         }
42630         return this.valueOff;
42631         
42632     },
42633
42634         // private
42635     onClick : function(){ 
42636         if (this.disabled) {
42637             return;
42638         }
42639         this.setChecked(!this.checked);
42640
42641         //if(this.el.dom.checked != this.checked){
42642         //    this.setValue(this.el.dom.checked);
42643        // }
42644     },
42645
42646     /**
42647      * Sets the checked state of the checkbox.
42648      * On is always based on a string comparison between inputValue and the param.
42649      * @param {Boolean/String} value - the value to set 
42650      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
42651      */
42652     setValue : function(v,suppressEvent){
42653         
42654         
42655         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
42656         //if(this.el && this.el.dom){
42657         //    this.el.dom.checked = this.checked;
42658         //    this.el.dom.defaultChecked = this.checked;
42659         //}
42660         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
42661         //this.fireEvent("check", this, this.checked);
42662     },
42663     // private..
42664     setChecked : function(state,suppressEvent)
42665     {
42666         if (this.inSetChecked) {
42667             this.checked = state;
42668             return;
42669         }
42670         
42671     
42672         if(this.wrap){
42673             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
42674         }
42675         this.checked = state;
42676         if(suppressEvent !== true){
42677             this.fireEvent('check', this, state);
42678         }
42679         this.inSetChecked = true;
42680         this.el.dom.value = state ? this.inputValue : this.valueOff;
42681         this.inSetChecked = false;
42682         
42683     },
42684     // handle setting of hidden value by some other method!!?!?
42685     setFromHidden: function()
42686     {
42687         if(!this.el){
42688             return;
42689         }
42690         //console.log("SET FROM HIDDEN");
42691         //alert('setFrom hidden');
42692         this.setValue(this.el.dom.value);
42693     },
42694     
42695     onDestroy : function()
42696     {
42697         if(this.viewEl){
42698             Roo.get(this.viewEl).remove();
42699         }
42700          
42701         Roo.form.Checkbox.superclass.onDestroy.call(this);
42702     },
42703     
42704     setBoxLabel : function(str)
42705     {
42706         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
42707     }
42708
42709 });/*
42710  * Based on:
42711  * Ext JS Library 1.1.1
42712  * Copyright(c) 2006-2007, Ext JS, LLC.
42713  *
42714  * Originally Released Under LGPL - original licence link has changed is not relivant.
42715  *
42716  * Fork - LGPL
42717  * <script type="text/javascript">
42718  */
42719  
42720 /**
42721  * @class Roo.form.Radio
42722  * @extends Roo.form.Checkbox
42723  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
42724  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
42725  * @constructor
42726  * Creates a new Radio
42727  * @param {Object} config Configuration options
42728  */
42729 Roo.form.Radio = function(){
42730     Roo.form.Radio.superclass.constructor.apply(this, arguments);
42731 };
42732 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
42733     inputType: 'radio',
42734
42735     /**
42736      * If this radio is part of a group, it will return the selected value
42737      * @return {String}
42738      */
42739     getGroupValue : function(){
42740         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
42741     },
42742     
42743     
42744     onRender : function(ct, position){
42745         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42746         
42747         if(this.inputValue !== undefined){
42748             this.el.dom.value = this.inputValue;
42749         }
42750          
42751         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42752         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42753         //var viewEl = this.wrap.createChild({ 
42754         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42755         //this.viewEl = viewEl;   
42756         //this.wrap.on('click', this.onClick,  this); 
42757         
42758         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42759         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
42760         
42761         
42762         
42763         if(this.boxLabel){
42764             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42765         //    viewEl.on('click', this.onClick,  this); 
42766         }
42767          if(this.checked){
42768             this.el.dom.checked =   'checked' ;
42769         }
42770          
42771     } 
42772     
42773     
42774 });//<script type="text/javascript">
42775
42776 /*
42777  * Based  Ext JS Library 1.1.1
42778  * Copyright(c) 2006-2007, Ext JS, LLC.
42779  * LGPL
42780  *
42781  */
42782  
42783 /**
42784  * @class Roo.HtmlEditorCore
42785  * @extends Roo.Component
42786  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
42787  *
42788  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42789  */
42790
42791 Roo.HtmlEditorCore = function(config){
42792     
42793     
42794     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
42795     
42796     
42797     this.addEvents({
42798         /**
42799          * @event initialize
42800          * Fires when the editor is fully initialized (including the iframe)
42801          * @param {Roo.HtmlEditorCore} this
42802          */
42803         initialize: true,
42804         /**
42805          * @event activate
42806          * Fires when the editor is first receives the focus. Any insertion must wait
42807          * until after this event.
42808          * @param {Roo.HtmlEditorCore} this
42809          */
42810         activate: true,
42811          /**
42812          * @event beforesync
42813          * Fires before the textarea is updated with content from the editor iframe. Return false
42814          * to cancel the sync.
42815          * @param {Roo.HtmlEditorCore} this
42816          * @param {String} html
42817          */
42818         beforesync: true,
42819          /**
42820          * @event beforepush
42821          * Fires before the iframe editor is updated with content from the textarea. Return false
42822          * to cancel the push.
42823          * @param {Roo.HtmlEditorCore} this
42824          * @param {String} html
42825          */
42826         beforepush: true,
42827          /**
42828          * @event sync
42829          * Fires when the textarea is updated with content from the editor iframe.
42830          * @param {Roo.HtmlEditorCore} this
42831          * @param {String} html
42832          */
42833         sync: true,
42834          /**
42835          * @event push
42836          * Fires when the iframe editor is updated with content from the textarea.
42837          * @param {Roo.HtmlEditorCore} this
42838          * @param {String} html
42839          */
42840         push: true,
42841         
42842         /**
42843          * @event editorevent
42844          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42845          * @param {Roo.HtmlEditorCore} this
42846          */
42847         editorevent: true
42848         
42849     });
42850     
42851     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
42852     
42853     // defaults : white / black...
42854     this.applyBlacklists();
42855     
42856     
42857     
42858 };
42859
42860
42861 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
42862
42863
42864      /**
42865      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
42866      */
42867     
42868     owner : false,
42869     
42870      /**
42871      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42872      *                        Roo.resizable.
42873      */
42874     resizable : false,
42875      /**
42876      * @cfg {Number} height (in pixels)
42877      */   
42878     height: 300,
42879    /**
42880      * @cfg {Number} width (in pixels)
42881      */   
42882     width: 500,
42883     
42884     /**
42885      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42886      * 
42887      */
42888     stylesheets: false,
42889     
42890     // id of frame..
42891     frameId: false,
42892     
42893     // private properties
42894     validationEvent : false,
42895     deferHeight: true,
42896     initialized : false,
42897     activated : false,
42898     sourceEditMode : false,
42899     onFocus : Roo.emptyFn,
42900     iframePad:3,
42901     hideMode:'offsets',
42902     
42903     clearUp: true,
42904     
42905     // blacklist + whitelisted elements..
42906     black: false,
42907     white: false,
42908      
42909     bodyCls : '',
42910
42911     /**
42912      * Protected method that will not generally be called directly. It
42913      * is called when the editor initializes the iframe with HTML contents. Override this method if you
42914      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
42915      */
42916     getDocMarkup : function(){
42917         // body styles..
42918         var st = '';
42919         
42920         // inherit styels from page...?? 
42921         if (this.stylesheets === false) {
42922             
42923             Roo.get(document.head).select('style').each(function(node) {
42924                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42925             });
42926             
42927             Roo.get(document.head).select('link').each(function(node) { 
42928                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42929             });
42930             
42931         } else if (!this.stylesheets.length) {
42932                 // simple..
42933                 st = '<style type="text/css">' +
42934                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42935                    '</style>';
42936         } else { 
42937             st = '<style type="text/css">' +
42938                     this.stylesheets +
42939                 '</style>';
42940         }
42941         
42942         st +=  '<style type="text/css">' +
42943             'IMG { cursor: pointer } ' +
42944         '</style>';
42945
42946         var cls = 'roo-htmleditor-body';
42947         
42948         if(this.bodyCls.length){
42949             cls += ' ' + this.bodyCls;
42950         }
42951         
42952         return '<html><head>' + st  +
42953             //<style type="text/css">' +
42954             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42955             //'</style>' +
42956             ' </head><body class="' +  cls + '"></body></html>';
42957     },
42958
42959     // private
42960     onRender : function(ct, position)
42961     {
42962         var _t = this;
42963         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
42964         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
42965         
42966         
42967         this.el.dom.style.border = '0 none';
42968         this.el.dom.setAttribute('tabIndex', -1);
42969         this.el.addClass('x-hidden hide');
42970         
42971         
42972         
42973         if(Roo.isIE){ // fix IE 1px bogus margin
42974             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
42975         }
42976        
42977         
42978         this.frameId = Roo.id();
42979         
42980          
42981         
42982         var iframe = this.owner.wrap.createChild({
42983             tag: 'iframe',
42984             cls: 'form-control', // bootstrap..
42985             id: this.frameId,
42986             name: this.frameId,
42987             frameBorder : 'no',
42988             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
42989         }, this.el
42990         );
42991         
42992         
42993         this.iframe = iframe.dom;
42994
42995          this.assignDocWin();
42996         
42997         this.doc.designMode = 'on';
42998        
42999         this.doc.open();
43000         this.doc.write(this.getDocMarkup());
43001         this.doc.close();
43002
43003         
43004         var task = { // must defer to wait for browser to be ready
43005             run : function(){
43006                 //console.log("run task?" + this.doc.readyState);
43007                 this.assignDocWin();
43008                 if(this.doc.body || this.doc.readyState == 'complete'){
43009                     try {
43010                         this.doc.designMode="on";
43011                     } catch (e) {
43012                         return;
43013                     }
43014                     Roo.TaskMgr.stop(task);
43015                     this.initEditor.defer(10, this);
43016                 }
43017             },
43018             interval : 10,
43019             duration: 10000,
43020             scope: this
43021         };
43022         Roo.TaskMgr.start(task);
43023
43024     },
43025
43026     // private
43027     onResize : function(w, h)
43028     {
43029          Roo.log('resize: ' +w + ',' + h );
43030         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
43031         if(!this.iframe){
43032             return;
43033         }
43034         if(typeof w == 'number'){
43035             
43036             this.iframe.style.width = w + 'px';
43037         }
43038         if(typeof h == 'number'){
43039             
43040             this.iframe.style.height = h + 'px';
43041             if(this.doc){
43042                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
43043             }
43044         }
43045         
43046     },
43047
43048     /**
43049      * Toggles the editor between standard and source edit mode.
43050      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43051      */
43052     toggleSourceEdit : function(sourceEditMode){
43053         
43054         this.sourceEditMode = sourceEditMode === true;
43055         
43056         if(this.sourceEditMode){
43057  
43058             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
43059             
43060         }else{
43061             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
43062             //this.iframe.className = '';
43063             this.deferFocus();
43064         }
43065         //this.setSize(this.owner.wrap.getSize());
43066         //this.fireEvent('editmodechange', this, this.sourceEditMode);
43067     },
43068
43069     
43070   
43071
43072     /**
43073      * Protected method that will not generally be called directly. If you need/want
43074      * custom HTML cleanup, this is the method you should override.
43075      * @param {String} html The HTML to be cleaned
43076      * return {String} The cleaned HTML
43077      */
43078     cleanHtml : function(html){
43079         html = String(html);
43080         if(html.length > 5){
43081             if(Roo.isSafari){ // strip safari nonsense
43082                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
43083             }
43084         }
43085         if(html == '&nbsp;'){
43086             html = '';
43087         }
43088         return html;
43089     },
43090
43091     /**
43092      * HTML Editor -> Textarea
43093      * Protected method that will not generally be called directly. Syncs the contents
43094      * of the editor iframe with the textarea.
43095      */
43096     syncValue : function(){
43097         if(this.initialized){
43098             var bd = (this.doc.body || this.doc.documentElement);
43099             //this.cleanUpPaste(); -- this is done else where and causes havoc..
43100             var html = bd.innerHTML;
43101             if(Roo.isSafari){
43102                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43103                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43104                 if(m && m[1]){
43105                     html = '<div style="'+m[0]+'">' + html + '</div>';
43106                 }
43107             }
43108             html = this.cleanHtml(html);
43109             // fix up the special chars.. normaly like back quotes in word...
43110             // however we do not want to do this with chinese..
43111             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
43112                 var cc = b.charCodeAt();
43113                 if (
43114                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43115                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43116                     (cc >= 0xf900 && cc < 0xfb00 )
43117                 ) {
43118                         return b;
43119                 }
43120                 return "&#"+cc+";" 
43121             });
43122             if(this.owner.fireEvent('beforesync', this, html) !== false){
43123                 this.el.dom.value = html;
43124                 this.owner.fireEvent('sync', this, html);
43125             }
43126         }
43127     },
43128
43129     /**
43130      * Protected method that will not generally be called directly. Pushes the value of the textarea
43131      * into the iframe editor.
43132      */
43133     pushValue : function(){
43134         if(this.initialized){
43135             var v = this.el.dom.value.trim();
43136             
43137 //            if(v.length < 1){
43138 //                v = '&#160;';
43139 //            }
43140             
43141             if(this.owner.fireEvent('beforepush', this, v) !== false){
43142                 var d = (this.doc.body || this.doc.documentElement);
43143                 d.innerHTML = v;
43144                 this.cleanUpPaste();
43145                 this.el.dom.value = d.innerHTML;
43146                 this.owner.fireEvent('push', this, v);
43147             }
43148         }
43149     },
43150
43151     // private
43152     deferFocus : function(){
43153         this.focus.defer(10, this);
43154     },
43155
43156     // doc'ed in Field
43157     focus : function(){
43158         if(this.win && !this.sourceEditMode){
43159             this.win.focus();
43160         }else{
43161             this.el.focus();
43162         }
43163     },
43164     
43165     assignDocWin: function()
43166     {
43167         var iframe = this.iframe;
43168         
43169          if(Roo.isIE){
43170             this.doc = iframe.contentWindow.document;
43171             this.win = iframe.contentWindow;
43172         } else {
43173 //            if (!Roo.get(this.frameId)) {
43174 //                return;
43175 //            }
43176 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43177 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43178             
43179             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43180                 return;
43181             }
43182             
43183             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43184             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43185         }
43186     },
43187     
43188     // private
43189     initEditor : function(){
43190         //console.log("INIT EDITOR");
43191         this.assignDocWin();
43192         
43193         
43194         
43195         this.doc.designMode="on";
43196         this.doc.open();
43197         this.doc.write(this.getDocMarkup());
43198         this.doc.close();
43199         
43200         var dbody = (this.doc.body || this.doc.documentElement);
43201         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43202         // this copies styles from the containing element into thsi one..
43203         // not sure why we need all of this..
43204         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43205         
43206         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43207         //ss['background-attachment'] = 'fixed'; // w3c
43208         dbody.bgProperties = 'fixed'; // ie
43209         //Roo.DomHelper.applyStyles(dbody, ss);
43210         Roo.EventManager.on(this.doc, {
43211             //'mousedown': this.onEditorEvent,
43212             'mouseup': this.onEditorEvent,
43213             'dblclick': this.onEditorEvent,
43214             'click': this.onEditorEvent,
43215             'keyup': this.onEditorEvent,
43216             buffer:100,
43217             scope: this
43218         });
43219         if(Roo.isGecko){
43220             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43221         }
43222         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43223             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43224         }
43225         this.initialized = true;
43226
43227         this.owner.fireEvent('initialize', this);
43228         this.pushValue();
43229     },
43230
43231     // private
43232     onDestroy : function(){
43233         
43234         
43235         
43236         if(this.rendered){
43237             
43238             //for (var i =0; i < this.toolbars.length;i++) {
43239             //    // fixme - ask toolbars for heights?
43240             //    this.toolbars[i].onDestroy();
43241            // }
43242             
43243             //this.wrap.dom.innerHTML = '';
43244             //this.wrap.remove();
43245         }
43246     },
43247
43248     // private
43249     onFirstFocus : function(){
43250         
43251         this.assignDocWin();
43252         
43253         
43254         this.activated = true;
43255          
43256     
43257         if(Roo.isGecko){ // prevent silly gecko errors
43258             this.win.focus();
43259             var s = this.win.getSelection();
43260             if(!s.focusNode || s.focusNode.nodeType != 3){
43261                 var r = s.getRangeAt(0);
43262                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43263                 r.collapse(true);
43264                 this.deferFocus();
43265             }
43266             try{
43267                 this.execCmd('useCSS', true);
43268                 this.execCmd('styleWithCSS', false);
43269             }catch(e){}
43270         }
43271         this.owner.fireEvent('activate', this);
43272     },
43273
43274     // private
43275     adjustFont: function(btn){
43276         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
43277         //if(Roo.isSafari){ // safari
43278         //    adjust *= 2;
43279        // }
43280         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
43281         if(Roo.isSafari){ // safari
43282             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
43283             v =  (v < 10) ? 10 : v;
43284             v =  (v > 48) ? 48 : v;
43285             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
43286             
43287         }
43288         
43289         
43290         v = Math.max(1, v+adjust);
43291         
43292         this.execCmd('FontSize', v  );
43293     },
43294
43295     onEditorEvent : function(e)
43296     {
43297         this.owner.fireEvent('editorevent', this, e);
43298       //  this.updateToolbar();
43299         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
43300     },
43301
43302     insertTag : function(tg)
43303     {
43304         // could be a bit smarter... -> wrap the current selected tRoo..
43305         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
43306             
43307             range = this.createRange(this.getSelection());
43308             var wrappingNode = this.doc.createElement(tg.toLowerCase());
43309             wrappingNode.appendChild(range.extractContents());
43310             range.insertNode(wrappingNode);
43311
43312             return;
43313             
43314             
43315             
43316         }
43317         this.execCmd("formatblock",   tg);
43318         
43319     },
43320     
43321     insertText : function(txt)
43322     {
43323         
43324         
43325         var range = this.createRange();
43326         range.deleteContents();
43327                //alert(Sender.getAttribute('label'));
43328                
43329         range.insertNode(this.doc.createTextNode(txt));
43330     } ,
43331     
43332      
43333
43334     /**
43335      * Executes a Midas editor command on the editor document and performs necessary focus and
43336      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
43337      * @param {String} cmd The Midas command
43338      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43339      */
43340     relayCmd : function(cmd, value){
43341         this.win.focus();
43342         this.execCmd(cmd, value);
43343         this.owner.fireEvent('editorevent', this);
43344         //this.updateToolbar();
43345         this.owner.deferFocus();
43346     },
43347
43348     /**
43349      * Executes a Midas editor command directly on the editor document.
43350      * For visual commands, you should use {@link #relayCmd} instead.
43351      * <b>This should only be called after the editor is initialized.</b>
43352      * @param {String} cmd The Midas command
43353      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43354      */
43355     execCmd : function(cmd, value){
43356         this.doc.execCommand(cmd, false, value === undefined ? null : value);
43357         this.syncValue();
43358     },
43359  
43360  
43361    
43362     /**
43363      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
43364      * to insert tRoo.
43365      * @param {String} text | dom node.. 
43366      */
43367     insertAtCursor : function(text)
43368     {
43369         
43370         if(!this.activated){
43371             return;
43372         }
43373         /*
43374         if(Roo.isIE){
43375             this.win.focus();
43376             var r = this.doc.selection.createRange();
43377             if(r){
43378                 r.collapse(true);
43379                 r.pasteHTML(text);
43380                 this.syncValue();
43381                 this.deferFocus();
43382             
43383             }
43384             return;
43385         }
43386         */
43387         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
43388             this.win.focus();
43389             
43390             
43391             // from jquery ui (MIT licenced)
43392             var range, node;
43393             var win = this.win;
43394             
43395             if (win.getSelection && win.getSelection().getRangeAt) {
43396                 range = win.getSelection().getRangeAt(0);
43397                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
43398                 range.insertNode(node);
43399             } else if (win.document.selection && win.document.selection.createRange) {
43400                 // no firefox support
43401                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43402                 win.document.selection.createRange().pasteHTML(txt);
43403             } else {
43404                 // no firefox support
43405                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43406                 this.execCmd('InsertHTML', txt);
43407             } 
43408             
43409             this.syncValue();
43410             
43411             this.deferFocus();
43412         }
43413     },
43414  // private
43415     mozKeyPress : function(e){
43416         if(e.ctrlKey){
43417             var c = e.getCharCode(), cmd;
43418           
43419             if(c > 0){
43420                 c = String.fromCharCode(c).toLowerCase();
43421                 switch(c){
43422                     case 'b':
43423                         cmd = 'bold';
43424                         break;
43425                     case 'i':
43426                         cmd = 'italic';
43427                         break;
43428                     
43429                     case 'u':
43430                         cmd = 'underline';
43431                         break;
43432                     
43433                     case 'v':
43434                         this.cleanUpPaste.defer(100, this);
43435                         return;
43436                         
43437                 }
43438                 if(cmd){
43439                     this.win.focus();
43440                     this.execCmd(cmd);
43441                     this.deferFocus();
43442                     e.preventDefault();
43443                 }
43444                 
43445             }
43446         }
43447     },
43448
43449     // private
43450     fixKeys : function(){ // load time branching for fastest keydown performance
43451         if(Roo.isIE){
43452             return function(e){
43453                 var k = e.getKey(), r;
43454                 if(k == e.TAB){
43455                     e.stopEvent();
43456                     r = this.doc.selection.createRange();
43457                     if(r){
43458                         r.collapse(true);
43459                         r.pasteHTML('&#160;&#160;&#160;&#160;');
43460                         this.deferFocus();
43461                     }
43462                     return;
43463                 }
43464                 
43465                 if(k == e.ENTER){
43466                     r = this.doc.selection.createRange();
43467                     if(r){
43468                         var target = r.parentElement();
43469                         if(!target || target.tagName.toLowerCase() != 'li'){
43470                             e.stopEvent();
43471                             r.pasteHTML('<br />');
43472                             r.collapse(false);
43473                             r.select();
43474                         }
43475                     }
43476                 }
43477                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43478                     this.cleanUpPaste.defer(100, this);
43479                     return;
43480                 }
43481                 
43482                 
43483             };
43484         }else if(Roo.isOpera){
43485             return function(e){
43486                 var k = e.getKey();
43487                 if(k == e.TAB){
43488                     e.stopEvent();
43489                     this.win.focus();
43490                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
43491                     this.deferFocus();
43492                 }
43493                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43494                     this.cleanUpPaste.defer(100, this);
43495                     return;
43496                 }
43497                 
43498             };
43499         }else if(Roo.isSafari){
43500             return function(e){
43501                 var k = e.getKey();
43502                 
43503                 if(k == e.TAB){
43504                     e.stopEvent();
43505                     this.execCmd('InsertText','\t');
43506                     this.deferFocus();
43507                     return;
43508                 }
43509                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43510                     this.cleanUpPaste.defer(100, this);
43511                     return;
43512                 }
43513                 
43514              };
43515         }
43516     }(),
43517     
43518     getAllAncestors: function()
43519     {
43520         var p = this.getSelectedNode();
43521         var a = [];
43522         if (!p) {
43523             a.push(p); // push blank onto stack..
43524             p = this.getParentElement();
43525         }
43526         
43527         
43528         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
43529             a.push(p);
43530             p = p.parentNode;
43531         }
43532         a.push(this.doc.body);
43533         return a;
43534     },
43535     lastSel : false,
43536     lastSelNode : false,
43537     
43538     
43539     getSelection : function() 
43540     {
43541         this.assignDocWin();
43542         return Roo.isIE ? this.doc.selection : this.win.getSelection();
43543     },
43544     
43545     getSelectedNode: function() 
43546     {
43547         // this may only work on Gecko!!!
43548         
43549         // should we cache this!!!!
43550         
43551         
43552         
43553          
43554         var range = this.createRange(this.getSelection()).cloneRange();
43555         
43556         if (Roo.isIE) {
43557             var parent = range.parentElement();
43558             while (true) {
43559                 var testRange = range.duplicate();
43560                 testRange.moveToElementText(parent);
43561                 if (testRange.inRange(range)) {
43562                     break;
43563                 }
43564                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
43565                     break;
43566                 }
43567                 parent = parent.parentElement;
43568             }
43569             return parent;
43570         }
43571         
43572         // is ancestor a text element.
43573         var ac =  range.commonAncestorContainer;
43574         if (ac.nodeType == 3) {
43575             ac = ac.parentNode;
43576         }
43577         
43578         var ar = ac.childNodes;
43579          
43580         var nodes = [];
43581         var other_nodes = [];
43582         var has_other_nodes = false;
43583         for (var i=0;i<ar.length;i++) {
43584             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
43585                 continue;
43586             }
43587             // fullly contained node.
43588             
43589             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
43590                 nodes.push(ar[i]);
43591                 continue;
43592             }
43593             
43594             // probably selected..
43595             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
43596                 other_nodes.push(ar[i]);
43597                 continue;
43598             }
43599             // outer..
43600             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
43601                 continue;
43602             }
43603             
43604             
43605             has_other_nodes = true;
43606         }
43607         if (!nodes.length && other_nodes.length) {
43608             nodes= other_nodes;
43609         }
43610         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
43611             return false;
43612         }
43613         
43614         return nodes[0];
43615     },
43616     createRange: function(sel)
43617     {
43618         // this has strange effects when using with 
43619         // top toolbar - not sure if it's a great idea.
43620         //this.editor.contentWindow.focus();
43621         if (typeof sel != "undefined") {
43622             try {
43623                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
43624             } catch(e) {
43625                 return this.doc.createRange();
43626             }
43627         } else {
43628             return this.doc.createRange();
43629         }
43630     },
43631     getParentElement: function()
43632     {
43633         
43634         this.assignDocWin();
43635         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
43636         
43637         var range = this.createRange(sel);
43638          
43639         try {
43640             var p = range.commonAncestorContainer;
43641             while (p.nodeType == 3) { // text node
43642                 p = p.parentNode;
43643             }
43644             return p;
43645         } catch (e) {
43646             return null;
43647         }
43648     
43649     },
43650     /***
43651      *
43652      * Range intersection.. the hard stuff...
43653      *  '-1' = before
43654      *  '0' = hits..
43655      *  '1' = after.
43656      *         [ -- selected range --- ]
43657      *   [fail]                        [fail]
43658      *
43659      *    basically..
43660      *      if end is before start or  hits it. fail.
43661      *      if start is after end or hits it fail.
43662      *
43663      *   if either hits (but other is outside. - then it's not 
43664      *   
43665      *    
43666      **/
43667     
43668     
43669     // @see http://www.thismuchiknow.co.uk/?p=64.
43670     rangeIntersectsNode : function(range, node)
43671     {
43672         var nodeRange = node.ownerDocument.createRange();
43673         try {
43674             nodeRange.selectNode(node);
43675         } catch (e) {
43676             nodeRange.selectNodeContents(node);
43677         }
43678     
43679         var rangeStartRange = range.cloneRange();
43680         rangeStartRange.collapse(true);
43681     
43682         var rangeEndRange = range.cloneRange();
43683         rangeEndRange.collapse(false);
43684     
43685         var nodeStartRange = nodeRange.cloneRange();
43686         nodeStartRange.collapse(true);
43687     
43688         var nodeEndRange = nodeRange.cloneRange();
43689         nodeEndRange.collapse(false);
43690     
43691         return rangeStartRange.compareBoundaryPoints(
43692                  Range.START_TO_START, nodeEndRange) == -1 &&
43693                rangeEndRange.compareBoundaryPoints(
43694                  Range.START_TO_START, nodeStartRange) == 1;
43695         
43696          
43697     },
43698     rangeCompareNode : function(range, node)
43699     {
43700         var nodeRange = node.ownerDocument.createRange();
43701         try {
43702             nodeRange.selectNode(node);
43703         } catch (e) {
43704             nodeRange.selectNodeContents(node);
43705         }
43706         
43707         
43708         range.collapse(true);
43709     
43710         nodeRange.collapse(true);
43711      
43712         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
43713         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
43714          
43715         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
43716         
43717         var nodeIsBefore   =  ss == 1;
43718         var nodeIsAfter    = ee == -1;
43719         
43720         if (nodeIsBefore && nodeIsAfter) {
43721             return 0; // outer
43722         }
43723         if (!nodeIsBefore && nodeIsAfter) {
43724             return 1; //right trailed.
43725         }
43726         
43727         if (nodeIsBefore && !nodeIsAfter) {
43728             return 2;  // left trailed.
43729         }
43730         // fully contined.
43731         return 3;
43732     },
43733
43734     // private? - in a new class?
43735     cleanUpPaste :  function()
43736     {
43737         // cleans up the whole document..
43738         Roo.log('cleanuppaste');
43739         
43740         this.cleanUpChildren(this.doc.body);
43741         var clean = this.cleanWordChars(this.doc.body.innerHTML);
43742         if (clean != this.doc.body.innerHTML) {
43743             this.doc.body.innerHTML = clean;
43744         }
43745         
43746     },
43747     
43748     cleanWordChars : function(input) {// change the chars to hex code
43749         var he = Roo.HtmlEditorCore;
43750         
43751         var output = input;
43752         Roo.each(he.swapCodes, function(sw) { 
43753             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
43754             
43755             output = output.replace(swapper, sw[1]);
43756         });
43757         
43758         return output;
43759     },
43760     
43761     
43762     cleanUpChildren : function (n)
43763     {
43764         if (!n.childNodes.length) {
43765             return;
43766         }
43767         for (var i = n.childNodes.length-1; i > -1 ; i--) {
43768            this.cleanUpChild(n.childNodes[i]);
43769         }
43770     },
43771     
43772     
43773         
43774     
43775     cleanUpChild : function (node)
43776     {
43777         var ed = this;
43778         //console.log(node);
43779         if (node.nodeName == "#text") {
43780             // clean up silly Windows -- stuff?
43781             return; 
43782         }
43783         if (node.nodeName == "#comment") {
43784             node.parentNode.removeChild(node);
43785             // clean up silly Windows -- stuff?
43786             return; 
43787         }
43788         var lcname = node.tagName.toLowerCase();
43789         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
43790         // whitelist of tags..
43791         
43792         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
43793             // remove node.
43794             node.parentNode.removeChild(node);
43795             return;
43796             
43797         }
43798         
43799         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
43800         
43801         // remove <a name=....> as rendering on yahoo mailer is borked with this.
43802         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
43803         
43804         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
43805         //    remove_keep_children = true;
43806         //}
43807         
43808         if (remove_keep_children) {
43809             this.cleanUpChildren(node);
43810             // inserts everything just before this node...
43811             while (node.childNodes.length) {
43812                 var cn = node.childNodes[0];
43813                 node.removeChild(cn);
43814                 node.parentNode.insertBefore(cn, node);
43815             }
43816             node.parentNode.removeChild(node);
43817             return;
43818         }
43819         
43820         if (!node.attributes || !node.attributes.length) {
43821             this.cleanUpChildren(node);
43822             return;
43823         }
43824         
43825         function cleanAttr(n,v)
43826         {
43827             
43828             if (v.match(/^\./) || v.match(/^\//)) {
43829                 return;
43830             }
43831             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
43832                 return;
43833             }
43834             if (v.match(/^#/)) {
43835                 return;
43836             }
43837 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
43838             node.removeAttribute(n);
43839             
43840         }
43841         
43842         var cwhite = this.cwhite;
43843         var cblack = this.cblack;
43844             
43845         function cleanStyle(n,v)
43846         {
43847             if (v.match(/expression/)) { //XSS?? should we even bother..
43848                 node.removeAttribute(n);
43849                 return;
43850             }
43851             
43852             var parts = v.split(/;/);
43853             var clean = [];
43854             
43855             Roo.each(parts, function(p) {
43856                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
43857                 if (!p.length) {
43858                     return true;
43859                 }
43860                 var l = p.split(':').shift().replace(/\s+/g,'');
43861                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
43862                 
43863                 if ( cwhite.length && cblack.indexOf(l) > -1) {
43864 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43865                     //node.removeAttribute(n);
43866                     return true;
43867                 }
43868                 //Roo.log()
43869                 // only allow 'c whitelisted system attributes'
43870                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
43871 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43872                     //node.removeAttribute(n);
43873                     return true;
43874                 }
43875                 
43876                 
43877                  
43878                 
43879                 clean.push(p);
43880                 return true;
43881             });
43882             if (clean.length) { 
43883                 node.setAttribute(n, clean.join(';'));
43884             } else {
43885                 node.removeAttribute(n);
43886             }
43887             
43888         }
43889         
43890         
43891         for (var i = node.attributes.length-1; i > -1 ; i--) {
43892             var a = node.attributes[i];
43893             //console.log(a);
43894             
43895             if (a.name.toLowerCase().substr(0,2)=='on')  {
43896                 node.removeAttribute(a.name);
43897                 continue;
43898             }
43899             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
43900                 node.removeAttribute(a.name);
43901                 continue;
43902             }
43903             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
43904                 cleanAttr(a.name,a.value); // fixme..
43905                 continue;
43906             }
43907             if (a.name == 'style') {
43908                 cleanStyle(a.name,a.value);
43909                 continue;
43910             }
43911             /// clean up MS crap..
43912             // tecnically this should be a list of valid class'es..
43913             
43914             
43915             if (a.name == 'class') {
43916                 if (a.value.match(/^Mso/)) {
43917                     node.className = '';
43918                 }
43919                 
43920                 if (a.value.match(/^body$/)) {
43921                     node.className = '';
43922                 }
43923                 continue;
43924             }
43925             
43926             // style cleanup!?
43927             // class cleanup?
43928             
43929         }
43930         
43931         
43932         this.cleanUpChildren(node);
43933         
43934         
43935     },
43936     
43937     /**
43938      * Clean up MS wordisms...
43939      */
43940     cleanWord : function(node)
43941     {
43942         
43943         
43944         if (!node) {
43945             this.cleanWord(this.doc.body);
43946             return;
43947         }
43948         if (node.nodeName == "#text") {
43949             // clean up silly Windows -- stuff?
43950             return; 
43951         }
43952         if (node.nodeName == "#comment") {
43953             node.parentNode.removeChild(node);
43954             // clean up silly Windows -- stuff?
43955             return; 
43956         }
43957         
43958         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
43959             node.parentNode.removeChild(node);
43960             return;
43961         }
43962         
43963         // remove - but keep children..
43964         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
43965             while (node.childNodes.length) {
43966                 var cn = node.childNodes[0];
43967                 node.removeChild(cn);
43968                 node.parentNode.insertBefore(cn, node);
43969             }
43970             node.parentNode.removeChild(node);
43971             this.iterateChildren(node, this.cleanWord);
43972             return;
43973         }
43974         // clean styles
43975         if (node.className.length) {
43976             
43977             var cn = node.className.split(/\W+/);
43978             var cna = [];
43979             Roo.each(cn, function(cls) {
43980                 if (cls.match(/Mso[a-zA-Z]+/)) {
43981                     return;
43982                 }
43983                 cna.push(cls);
43984             });
43985             node.className = cna.length ? cna.join(' ') : '';
43986             if (!cna.length) {
43987                 node.removeAttribute("class");
43988             }
43989         }
43990         
43991         if (node.hasAttribute("lang")) {
43992             node.removeAttribute("lang");
43993         }
43994         
43995         if (node.hasAttribute("style")) {
43996             
43997             var styles = node.getAttribute("style").split(";");
43998             var nstyle = [];
43999             Roo.each(styles, function(s) {
44000                 if (!s.match(/:/)) {
44001                     return;
44002                 }
44003                 var kv = s.split(":");
44004                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44005                     return;
44006                 }
44007                 // what ever is left... we allow.
44008                 nstyle.push(s);
44009             });
44010             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44011             if (!nstyle.length) {
44012                 node.removeAttribute('style');
44013             }
44014         }
44015         this.iterateChildren(node, this.cleanWord);
44016         
44017         
44018         
44019     },
44020     /**
44021      * iterateChildren of a Node, calling fn each time, using this as the scole..
44022      * @param {DomNode} node node to iterate children of.
44023      * @param {Function} fn method of this class to call on each item.
44024      */
44025     iterateChildren : function(node, fn)
44026     {
44027         if (!node.childNodes.length) {
44028                 return;
44029         }
44030         for (var i = node.childNodes.length-1; i > -1 ; i--) {
44031            fn.call(this, node.childNodes[i])
44032         }
44033     },
44034     
44035     
44036     /**
44037      * cleanTableWidths.
44038      *
44039      * Quite often pasting from word etc.. results in tables with column and widths.
44040      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44041      *
44042      */
44043     cleanTableWidths : function(node)
44044     {
44045          
44046          
44047         if (!node) {
44048             this.cleanTableWidths(this.doc.body);
44049             return;
44050         }
44051         
44052         // ignore list...
44053         if (node.nodeName == "#text" || node.nodeName == "#comment") {
44054             return; 
44055         }
44056         Roo.log(node.tagName);
44057         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
44058             this.iterateChildren(node, this.cleanTableWidths);
44059             return;
44060         }
44061         if (node.hasAttribute('width')) {
44062             node.removeAttribute('width');
44063         }
44064         
44065          
44066         if (node.hasAttribute("style")) {
44067             // pretty basic...
44068             
44069             var styles = node.getAttribute("style").split(";");
44070             var nstyle = [];
44071             Roo.each(styles, function(s) {
44072                 if (!s.match(/:/)) {
44073                     return;
44074                 }
44075                 var kv = s.split(":");
44076                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
44077                     return;
44078                 }
44079                 // what ever is left... we allow.
44080                 nstyle.push(s);
44081             });
44082             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44083             if (!nstyle.length) {
44084                 node.removeAttribute('style');
44085             }
44086         }
44087         
44088         this.iterateChildren(node, this.cleanTableWidths);
44089         
44090         
44091     },
44092     
44093     
44094     
44095     
44096     domToHTML : function(currentElement, depth, nopadtext) {
44097         
44098         depth = depth || 0;
44099         nopadtext = nopadtext || false;
44100     
44101         if (!currentElement) {
44102             return this.domToHTML(this.doc.body);
44103         }
44104         
44105         //Roo.log(currentElement);
44106         var j;
44107         var allText = false;
44108         var nodeName = currentElement.nodeName;
44109         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
44110         
44111         if  (nodeName == '#text') {
44112             
44113             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
44114         }
44115         
44116         
44117         var ret = '';
44118         if (nodeName != 'BODY') {
44119              
44120             var i = 0;
44121             // Prints the node tagName, such as <A>, <IMG>, etc
44122             if (tagName) {
44123                 var attr = [];
44124                 for(i = 0; i < currentElement.attributes.length;i++) {
44125                     // quoting?
44126                     var aname = currentElement.attributes.item(i).name;
44127                     if (!currentElement.attributes.item(i).value.length) {
44128                         continue;
44129                     }
44130                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44131                 }
44132                 
44133                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44134             } 
44135             else {
44136                 
44137                 // eack
44138             }
44139         } else {
44140             tagName = false;
44141         }
44142         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44143             return ret;
44144         }
44145         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44146             nopadtext = true;
44147         }
44148         
44149         
44150         // Traverse the tree
44151         i = 0;
44152         var currentElementChild = currentElement.childNodes.item(i);
44153         var allText = true;
44154         var innerHTML  = '';
44155         lastnode = '';
44156         while (currentElementChild) {
44157             // Formatting code (indent the tree so it looks nice on the screen)
44158             var nopad = nopadtext;
44159             if (lastnode == 'SPAN') {
44160                 nopad  = true;
44161             }
44162             // text
44163             if  (currentElementChild.nodeName == '#text') {
44164                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44165                 toadd = nopadtext ? toadd : toadd.trim();
44166                 if (!nopad && toadd.length > 80) {
44167                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44168                 }
44169                 innerHTML  += toadd;
44170                 
44171                 i++;
44172                 currentElementChild = currentElement.childNodes.item(i);
44173                 lastNode = '';
44174                 continue;
44175             }
44176             allText = false;
44177             
44178             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44179                 
44180             // Recursively traverse the tree structure of the child node
44181             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44182             lastnode = currentElementChild.nodeName;
44183             i++;
44184             currentElementChild=currentElement.childNodes.item(i);
44185         }
44186         
44187         ret += innerHTML;
44188         
44189         if (!allText) {
44190                 // The remaining code is mostly for formatting the tree
44191             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44192         }
44193         
44194         
44195         if (tagName) {
44196             ret+= "</"+tagName+">";
44197         }
44198         return ret;
44199         
44200     },
44201         
44202     applyBlacklists : function()
44203     {
44204         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44205         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44206         
44207         this.white = [];
44208         this.black = [];
44209         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44210             if (b.indexOf(tag) > -1) {
44211                 return;
44212             }
44213             this.white.push(tag);
44214             
44215         }, this);
44216         
44217         Roo.each(w, function(tag) {
44218             if (b.indexOf(tag) > -1) {
44219                 return;
44220             }
44221             if (this.white.indexOf(tag) > -1) {
44222                 return;
44223             }
44224             this.white.push(tag);
44225             
44226         }, this);
44227         
44228         
44229         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44230             if (w.indexOf(tag) > -1) {
44231                 return;
44232             }
44233             this.black.push(tag);
44234             
44235         }, this);
44236         
44237         Roo.each(b, function(tag) {
44238             if (w.indexOf(tag) > -1) {
44239                 return;
44240             }
44241             if (this.black.indexOf(tag) > -1) {
44242                 return;
44243             }
44244             this.black.push(tag);
44245             
44246         }, this);
44247         
44248         
44249         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
44250         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
44251         
44252         this.cwhite = [];
44253         this.cblack = [];
44254         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
44255             if (b.indexOf(tag) > -1) {
44256                 return;
44257             }
44258             this.cwhite.push(tag);
44259             
44260         }, this);
44261         
44262         Roo.each(w, function(tag) {
44263             if (b.indexOf(tag) > -1) {
44264                 return;
44265             }
44266             if (this.cwhite.indexOf(tag) > -1) {
44267                 return;
44268             }
44269             this.cwhite.push(tag);
44270             
44271         }, this);
44272         
44273         
44274         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
44275             if (w.indexOf(tag) > -1) {
44276                 return;
44277             }
44278             this.cblack.push(tag);
44279             
44280         }, this);
44281         
44282         Roo.each(b, function(tag) {
44283             if (w.indexOf(tag) > -1) {
44284                 return;
44285             }
44286             if (this.cblack.indexOf(tag) > -1) {
44287                 return;
44288             }
44289             this.cblack.push(tag);
44290             
44291         }, this);
44292     },
44293     
44294     setStylesheets : function(stylesheets)
44295     {
44296         if(typeof(stylesheets) == 'string'){
44297             Roo.get(this.iframe.contentDocument.head).createChild({
44298                 tag : 'link',
44299                 rel : 'stylesheet',
44300                 type : 'text/css',
44301                 href : stylesheets
44302             });
44303             
44304             return;
44305         }
44306         var _this = this;
44307      
44308         Roo.each(stylesheets, function(s) {
44309             if(!s.length){
44310                 return;
44311             }
44312             
44313             Roo.get(_this.iframe.contentDocument.head).createChild({
44314                 tag : 'link',
44315                 rel : 'stylesheet',
44316                 type : 'text/css',
44317                 href : s
44318             });
44319         });
44320
44321         
44322     },
44323     
44324     removeStylesheets : function()
44325     {
44326         var _this = this;
44327         
44328         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
44329             s.remove();
44330         });
44331     },
44332     
44333     setStyle : function(style)
44334     {
44335         Roo.get(this.iframe.contentDocument.head).createChild({
44336             tag : 'style',
44337             type : 'text/css',
44338             html : style
44339         });
44340
44341         return;
44342     }
44343     
44344     // hide stuff that is not compatible
44345     /**
44346      * @event blur
44347      * @hide
44348      */
44349     /**
44350      * @event change
44351      * @hide
44352      */
44353     /**
44354      * @event focus
44355      * @hide
44356      */
44357     /**
44358      * @event specialkey
44359      * @hide
44360      */
44361     /**
44362      * @cfg {String} fieldClass @hide
44363      */
44364     /**
44365      * @cfg {String} focusClass @hide
44366      */
44367     /**
44368      * @cfg {String} autoCreate @hide
44369      */
44370     /**
44371      * @cfg {String} inputType @hide
44372      */
44373     /**
44374      * @cfg {String} invalidClass @hide
44375      */
44376     /**
44377      * @cfg {String} invalidText @hide
44378      */
44379     /**
44380      * @cfg {String} msgFx @hide
44381      */
44382     /**
44383      * @cfg {String} validateOnBlur @hide
44384      */
44385 });
44386
44387 Roo.HtmlEditorCore.white = [
44388         'area', 'br', 'img', 'input', 'hr', 'wbr',
44389         
44390        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
44391        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
44392        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
44393        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
44394        'table',   'ul',         'xmp', 
44395        
44396        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
44397       'thead',   'tr', 
44398      
44399       'dir', 'menu', 'ol', 'ul', 'dl',
44400        
44401       'embed',  'object'
44402 ];
44403
44404
44405 Roo.HtmlEditorCore.black = [
44406     //    'embed',  'object', // enable - backend responsiblity to clean thiese
44407         'applet', // 
44408         'base',   'basefont', 'bgsound', 'blink',  'body', 
44409         'frame',  'frameset', 'head',    'html',   'ilayer', 
44410         'iframe', 'layer',  'link',     'meta',    'object',   
44411         'script', 'style' ,'title',  'xml' // clean later..
44412 ];
44413 Roo.HtmlEditorCore.clean = [
44414     'script', 'style', 'title', 'xml'
44415 ];
44416 Roo.HtmlEditorCore.remove = [
44417     'font'
44418 ];
44419 // attributes..
44420
44421 Roo.HtmlEditorCore.ablack = [
44422     'on'
44423 ];
44424     
44425 Roo.HtmlEditorCore.aclean = [ 
44426     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
44427 ];
44428
44429 // protocols..
44430 Roo.HtmlEditorCore.pwhite= [
44431         'http',  'https',  'mailto'
44432 ];
44433
44434 // white listed style attributes.
44435 Roo.HtmlEditorCore.cwhite= [
44436       //  'text-align', /// default is to allow most things..
44437       
44438          
44439 //        'font-size'//??
44440 ];
44441
44442 // black listed style attributes.
44443 Roo.HtmlEditorCore.cblack= [
44444       //  'font-size' -- this can be set by the project 
44445 ];
44446
44447
44448 Roo.HtmlEditorCore.swapCodes   =[ 
44449     [    8211, "--" ], 
44450     [    8212, "--" ], 
44451     [    8216,  "'" ],  
44452     [    8217, "'" ],  
44453     [    8220, '"' ],  
44454     [    8221, '"' ],  
44455     [    8226, "*" ],  
44456     [    8230, "..." ]
44457 ]; 
44458
44459     //<script type="text/javascript">
44460
44461 /*
44462  * Ext JS Library 1.1.1
44463  * Copyright(c) 2006-2007, Ext JS, LLC.
44464  * Licence LGPL
44465  * 
44466  */
44467  
44468  
44469 Roo.form.HtmlEditor = function(config){
44470     
44471     
44472     
44473     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
44474     
44475     if (!this.toolbars) {
44476         this.toolbars = [];
44477     }
44478     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
44479     
44480     
44481 };
44482
44483 /**
44484  * @class Roo.form.HtmlEditor
44485  * @extends Roo.form.Field
44486  * Provides a lightweight HTML Editor component.
44487  *
44488  * This has been tested on Fireforx / Chrome.. IE may not be so great..
44489  * 
44490  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
44491  * supported by this editor.</b><br/><br/>
44492  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
44493  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
44494  */
44495 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
44496     /**
44497      * @cfg {Boolean} clearUp
44498      */
44499     clearUp : true,
44500       /**
44501      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
44502      */
44503     toolbars : false,
44504    
44505      /**
44506      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
44507      *                        Roo.resizable.
44508      */
44509     resizable : false,
44510      /**
44511      * @cfg {Number} height (in pixels)
44512      */   
44513     height: 300,
44514    /**
44515      * @cfg {Number} width (in pixels)
44516      */   
44517     width: 500,
44518     
44519     /**
44520      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
44521      * 
44522      */
44523     stylesheets: false,
44524     
44525     
44526      /**
44527      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
44528      * 
44529      */
44530     cblack: false,
44531     /**
44532      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
44533      * 
44534      */
44535     cwhite: false,
44536     
44537      /**
44538      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
44539      * 
44540      */
44541     black: false,
44542     /**
44543      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
44544      * 
44545      */
44546     white: false,
44547     
44548     // id of frame..
44549     frameId: false,
44550     
44551     // private properties
44552     validationEvent : false,
44553     deferHeight: true,
44554     initialized : false,
44555     activated : false,
44556     
44557     onFocus : Roo.emptyFn,
44558     iframePad:3,
44559     hideMode:'offsets',
44560     
44561     actionMode : 'container', // defaults to hiding it...
44562     
44563     defaultAutoCreate : { // modified by initCompnoent..
44564         tag: "textarea",
44565         style:"width:500px;height:300px;",
44566         autocomplete: "new-password"
44567     },
44568
44569     // private
44570     initComponent : function(){
44571         this.addEvents({
44572             /**
44573              * @event initialize
44574              * Fires when the editor is fully initialized (including the iframe)
44575              * @param {HtmlEditor} this
44576              */
44577             initialize: true,
44578             /**
44579              * @event activate
44580              * Fires when the editor is first receives the focus. Any insertion must wait
44581              * until after this event.
44582              * @param {HtmlEditor} this
44583              */
44584             activate: true,
44585              /**
44586              * @event beforesync
44587              * Fires before the textarea is updated with content from the editor iframe. Return false
44588              * to cancel the sync.
44589              * @param {HtmlEditor} this
44590              * @param {String} html
44591              */
44592             beforesync: true,
44593              /**
44594              * @event beforepush
44595              * Fires before the iframe editor is updated with content from the textarea. Return false
44596              * to cancel the push.
44597              * @param {HtmlEditor} this
44598              * @param {String} html
44599              */
44600             beforepush: true,
44601              /**
44602              * @event sync
44603              * Fires when the textarea is updated with content from the editor iframe.
44604              * @param {HtmlEditor} this
44605              * @param {String} html
44606              */
44607             sync: true,
44608              /**
44609              * @event push
44610              * Fires when the iframe editor is updated with content from the textarea.
44611              * @param {HtmlEditor} this
44612              * @param {String} html
44613              */
44614             push: true,
44615              /**
44616              * @event editmodechange
44617              * Fires when the editor switches edit modes
44618              * @param {HtmlEditor} this
44619              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
44620              */
44621             editmodechange: true,
44622             /**
44623              * @event editorevent
44624              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
44625              * @param {HtmlEditor} this
44626              */
44627             editorevent: true,
44628             /**
44629              * @event firstfocus
44630              * Fires when on first focus - needed by toolbars..
44631              * @param {HtmlEditor} this
44632              */
44633             firstfocus: true,
44634             /**
44635              * @event autosave
44636              * Auto save the htmlEditor value as a file into Events
44637              * @param {HtmlEditor} this
44638              */
44639             autosave: true,
44640             /**
44641              * @event savedpreview
44642              * preview the saved version of htmlEditor
44643              * @param {HtmlEditor} this
44644              */
44645             savedpreview: true,
44646             
44647             /**
44648             * @event stylesheetsclick
44649             * Fires when press the Sytlesheets button
44650             * @param {Roo.HtmlEditorCore} this
44651             */
44652             stylesheetsclick: true
44653         });
44654         this.defaultAutoCreate =  {
44655             tag: "textarea",
44656             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
44657             autocomplete: "new-password"
44658         };
44659     },
44660
44661     /**
44662      * Protected method that will not generally be called directly. It
44663      * is called when the editor creates its toolbar. Override this method if you need to
44664      * add custom toolbar buttons.
44665      * @param {HtmlEditor} editor
44666      */
44667     createToolbar : function(editor){
44668         Roo.log("create toolbars");
44669         if (!editor.toolbars || !editor.toolbars.length) {
44670             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
44671         }
44672         
44673         for (var i =0 ; i < editor.toolbars.length;i++) {
44674             editor.toolbars[i] = Roo.factory(
44675                     typeof(editor.toolbars[i]) == 'string' ?
44676                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
44677                 Roo.form.HtmlEditor);
44678             editor.toolbars[i].init(editor);
44679         }
44680          
44681         
44682     },
44683
44684      
44685     // private
44686     onRender : function(ct, position)
44687     {
44688         var _t = this;
44689         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
44690         
44691         this.wrap = this.el.wrap({
44692             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
44693         });
44694         
44695         this.editorcore.onRender(ct, position);
44696          
44697         if (this.resizable) {
44698             this.resizeEl = new Roo.Resizable(this.wrap, {
44699                 pinned : true,
44700                 wrap: true,
44701                 dynamic : true,
44702                 minHeight : this.height,
44703                 height: this.height,
44704                 handles : this.resizable,
44705                 width: this.width,
44706                 listeners : {
44707                     resize : function(r, w, h) {
44708                         _t.onResize(w,h); // -something
44709                     }
44710                 }
44711             });
44712             
44713         }
44714         this.createToolbar(this);
44715        
44716         
44717         if(!this.width){
44718             this.setSize(this.wrap.getSize());
44719         }
44720         if (this.resizeEl) {
44721             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
44722             // should trigger onReize..
44723         }
44724         
44725         this.keyNav = new Roo.KeyNav(this.el, {
44726             
44727             "tab" : function(e){
44728                 e.preventDefault();
44729                 
44730                 var value = this.getValue();
44731                 
44732                 var start = this.el.dom.selectionStart;
44733                 var end = this.el.dom.selectionEnd;
44734                 
44735                 if(!e.shiftKey){
44736                     
44737                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
44738                     this.el.dom.setSelectionRange(end + 1, end + 1);
44739                     return;
44740                 }
44741                 
44742                 var f = value.substring(0, start).split("\t");
44743                 
44744                 if(f.pop().length != 0){
44745                     return;
44746                 }
44747                 
44748                 this.setValue(f.join("\t") + value.substring(end));
44749                 this.el.dom.setSelectionRange(start - 1, start - 1);
44750                 
44751             },
44752             
44753             "home" : function(e){
44754                 e.preventDefault();
44755                 
44756                 var curr = this.el.dom.selectionStart;
44757                 var lines = this.getValue().split("\n");
44758                 
44759                 if(!lines.length){
44760                     return;
44761                 }
44762                 
44763                 if(e.ctrlKey){
44764                     this.el.dom.setSelectionRange(0, 0);
44765                     return;
44766                 }
44767                 
44768                 var pos = 0;
44769                 
44770                 for (var i = 0; i < lines.length;i++) {
44771                     pos += lines[i].length;
44772                     
44773                     if(i != 0){
44774                         pos += 1;
44775                     }
44776                     
44777                     if(pos < curr){
44778                         continue;
44779                     }
44780                     
44781                     pos -= lines[i].length;
44782                     
44783                     break;
44784                 }
44785                 
44786                 if(!e.shiftKey){
44787                     this.el.dom.setSelectionRange(pos, pos);
44788                     return;
44789                 }
44790                 
44791                 this.el.dom.selectionStart = pos;
44792                 this.el.dom.selectionEnd = curr;
44793             },
44794             
44795             "end" : function(e){
44796                 e.preventDefault();
44797                 
44798                 var curr = this.el.dom.selectionStart;
44799                 var lines = this.getValue().split("\n");
44800                 
44801                 if(!lines.length){
44802                     return;
44803                 }
44804                 
44805                 if(e.ctrlKey){
44806                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
44807                     return;
44808                 }
44809                 
44810                 var pos = 0;
44811                 
44812                 for (var i = 0; i < lines.length;i++) {
44813                     
44814                     pos += lines[i].length;
44815                     
44816                     if(i != 0){
44817                         pos += 1;
44818                     }
44819                     
44820                     if(pos < curr){
44821                         continue;
44822                     }
44823                     
44824                     break;
44825                 }
44826                 
44827                 if(!e.shiftKey){
44828                     this.el.dom.setSelectionRange(pos, pos);
44829                     return;
44830                 }
44831                 
44832                 this.el.dom.selectionStart = curr;
44833                 this.el.dom.selectionEnd = pos;
44834             },
44835
44836             scope : this,
44837
44838             doRelay : function(foo, bar, hname){
44839                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44840             },
44841
44842             forceKeyDown: true
44843         });
44844         
44845 //        if(this.autosave && this.w){
44846 //            this.autoSaveFn = setInterval(this.autosave, 1000);
44847 //        }
44848     },
44849
44850     // private
44851     onResize : function(w, h)
44852     {
44853         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
44854         var ew = false;
44855         var eh = false;
44856         
44857         if(this.el ){
44858             if(typeof w == 'number'){
44859                 var aw = w - this.wrap.getFrameWidth('lr');
44860                 this.el.setWidth(this.adjustWidth('textarea', aw));
44861                 ew = aw;
44862             }
44863             if(typeof h == 'number'){
44864                 var tbh = 0;
44865                 for (var i =0; i < this.toolbars.length;i++) {
44866                     // fixme - ask toolbars for heights?
44867                     tbh += this.toolbars[i].tb.el.getHeight();
44868                     if (this.toolbars[i].footer) {
44869                         tbh += this.toolbars[i].footer.el.getHeight();
44870                     }
44871                 }
44872                 
44873                 
44874                 
44875                 
44876                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
44877                 ah -= 5; // knock a few pixes off for look..
44878 //                Roo.log(ah);
44879                 this.el.setHeight(this.adjustWidth('textarea', ah));
44880                 var eh = ah;
44881             }
44882         }
44883         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
44884         this.editorcore.onResize(ew,eh);
44885         
44886     },
44887
44888     /**
44889      * Toggles the editor between standard and source edit mode.
44890      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
44891      */
44892     toggleSourceEdit : function(sourceEditMode)
44893     {
44894         this.editorcore.toggleSourceEdit(sourceEditMode);
44895         
44896         if(this.editorcore.sourceEditMode){
44897             Roo.log('editor - showing textarea');
44898             
44899 //            Roo.log('in');
44900 //            Roo.log(this.syncValue());
44901             this.editorcore.syncValue();
44902             this.el.removeClass('x-hidden');
44903             this.el.dom.removeAttribute('tabIndex');
44904             this.el.focus();
44905             
44906             for (var i = 0; i < this.toolbars.length; i++) {
44907                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44908                     this.toolbars[i].tb.hide();
44909                     this.toolbars[i].footer.hide();
44910                 }
44911             }
44912             
44913         }else{
44914             Roo.log('editor - hiding textarea');
44915 //            Roo.log('out')
44916 //            Roo.log(this.pushValue()); 
44917             this.editorcore.pushValue();
44918             
44919             this.el.addClass('x-hidden');
44920             this.el.dom.setAttribute('tabIndex', -1);
44921             
44922             for (var i = 0; i < this.toolbars.length; i++) {
44923                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44924                     this.toolbars[i].tb.show();
44925                     this.toolbars[i].footer.show();
44926                 }
44927             }
44928             
44929             //this.deferFocus();
44930         }
44931         
44932         this.setSize(this.wrap.getSize());
44933         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
44934         
44935         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
44936     },
44937  
44938     // private (for BoxComponent)
44939     adjustSize : Roo.BoxComponent.prototype.adjustSize,
44940
44941     // private (for BoxComponent)
44942     getResizeEl : function(){
44943         return this.wrap;
44944     },
44945
44946     // private (for BoxComponent)
44947     getPositionEl : function(){
44948         return this.wrap;
44949     },
44950
44951     // private
44952     initEvents : function(){
44953         this.originalValue = this.getValue();
44954     },
44955
44956     /**
44957      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44958      * @method
44959      */
44960     markInvalid : Roo.emptyFn,
44961     /**
44962      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44963      * @method
44964      */
44965     clearInvalid : Roo.emptyFn,
44966
44967     setValue : function(v){
44968         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
44969         this.editorcore.pushValue();
44970     },
44971
44972      
44973     // private
44974     deferFocus : function(){
44975         this.focus.defer(10, this);
44976     },
44977
44978     // doc'ed in Field
44979     focus : function(){
44980         this.editorcore.focus();
44981         
44982     },
44983       
44984
44985     // private
44986     onDestroy : function(){
44987         
44988         
44989         
44990         if(this.rendered){
44991             
44992             for (var i =0; i < this.toolbars.length;i++) {
44993                 // fixme - ask toolbars for heights?
44994                 this.toolbars[i].onDestroy();
44995             }
44996             
44997             this.wrap.dom.innerHTML = '';
44998             this.wrap.remove();
44999         }
45000     },
45001
45002     // private
45003     onFirstFocus : function(){
45004         //Roo.log("onFirstFocus");
45005         this.editorcore.onFirstFocus();
45006          for (var i =0; i < this.toolbars.length;i++) {
45007             this.toolbars[i].onFirstFocus();
45008         }
45009         
45010     },
45011     
45012     // private
45013     syncValue : function()
45014     {
45015         this.editorcore.syncValue();
45016     },
45017     
45018     pushValue : function()
45019     {
45020         this.editorcore.pushValue();
45021     },
45022     
45023     setStylesheets : function(stylesheets)
45024     {
45025         this.editorcore.setStylesheets(stylesheets);
45026     },
45027     
45028     removeStylesheets : function()
45029     {
45030         this.editorcore.removeStylesheets();
45031     }
45032      
45033     
45034     // hide stuff that is not compatible
45035     /**
45036      * @event blur
45037      * @hide
45038      */
45039     /**
45040      * @event change
45041      * @hide
45042      */
45043     /**
45044      * @event focus
45045      * @hide
45046      */
45047     /**
45048      * @event specialkey
45049      * @hide
45050      */
45051     /**
45052      * @cfg {String} fieldClass @hide
45053      */
45054     /**
45055      * @cfg {String} focusClass @hide
45056      */
45057     /**
45058      * @cfg {String} autoCreate @hide
45059      */
45060     /**
45061      * @cfg {String} inputType @hide
45062      */
45063     /**
45064      * @cfg {String} invalidClass @hide
45065      */
45066     /**
45067      * @cfg {String} invalidText @hide
45068      */
45069     /**
45070      * @cfg {String} msgFx @hide
45071      */
45072     /**
45073      * @cfg {String} validateOnBlur @hide
45074      */
45075 });
45076  
45077     // <script type="text/javascript">
45078 /*
45079  * Based on
45080  * Ext JS Library 1.1.1
45081  * Copyright(c) 2006-2007, Ext JS, LLC.
45082  *  
45083  
45084  */
45085
45086 /**
45087  * @class Roo.form.HtmlEditorToolbar1
45088  * Basic Toolbar
45089  * 
45090  * Usage:
45091  *
45092  new Roo.form.HtmlEditor({
45093     ....
45094     toolbars : [
45095         new Roo.form.HtmlEditorToolbar1({
45096             disable : { fonts: 1 , format: 1, ..., ... , ...],
45097             btns : [ .... ]
45098         })
45099     }
45100      
45101  * 
45102  * @cfg {Object} disable List of elements to disable..
45103  * @cfg {Array} btns List of additional buttons.
45104  * 
45105  * 
45106  * NEEDS Extra CSS? 
45107  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
45108  */
45109  
45110 Roo.form.HtmlEditor.ToolbarStandard = function(config)
45111 {
45112     
45113     Roo.apply(this, config);
45114     
45115     // default disabled, based on 'good practice'..
45116     this.disable = this.disable || {};
45117     Roo.applyIf(this.disable, {
45118         fontSize : true,
45119         colors : true,
45120         specialElements : true
45121     });
45122     
45123     
45124     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45125     // dont call parent... till later.
45126 }
45127
45128 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
45129     
45130     tb: false,
45131     
45132     rendered: false,
45133     
45134     editor : false,
45135     editorcore : false,
45136     /**
45137      * @cfg {Object} disable  List of toolbar elements to disable
45138          
45139      */
45140     disable : false,
45141     
45142     
45143      /**
45144      * @cfg {String} createLinkText The default text for the create link prompt
45145      */
45146     createLinkText : 'Please enter the URL for the link:',
45147     /**
45148      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45149      */
45150     defaultLinkValue : 'http:/'+'/',
45151    
45152     
45153       /**
45154      * @cfg {Array} fontFamilies An array of available font families
45155      */
45156     fontFamilies : [
45157         'Arial',
45158         'Courier New',
45159         'Tahoma',
45160         'Times New Roman',
45161         'Verdana'
45162     ],
45163     
45164     specialChars : [
45165            "&#169;",
45166           "&#174;",     
45167           "&#8482;",    
45168           "&#163;" ,    
45169          // "&#8212;",    
45170           "&#8230;",    
45171           "&#247;" ,    
45172         //  "&#225;" ,     ?? a acute?
45173            "&#8364;"    , //Euro
45174        //   "&#8220;"    ,
45175         //  "&#8221;"    ,
45176         //  "&#8226;"    ,
45177           "&#176;"  //   , // degrees
45178
45179          // "&#233;"     , // e ecute
45180          // "&#250;"     , // u ecute?
45181     ],
45182     
45183     specialElements : [
45184         {
45185             text: "Insert Table",
45186             xtype: 'MenuItem',
45187             xns : Roo.Menu,
45188             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45189                 
45190         },
45191         {    
45192             text: "Insert Image",
45193             xtype: 'MenuItem',
45194             xns : Roo.Menu,
45195             ihtml : '<img src="about:blank"/>'
45196             
45197         }
45198         
45199          
45200     ],
45201     
45202     
45203     inputElements : [ 
45204             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45205             "input:submit", "input:button", "select", "textarea", "label" ],
45206     formats : [
45207         ["p"] ,  
45208         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45209         ["pre"],[ "code"], 
45210         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45211         ['div'],['span']
45212     ],
45213     
45214     cleanStyles : [
45215         "font-size"
45216     ],
45217      /**
45218      * @cfg {String} defaultFont default font to use.
45219      */
45220     defaultFont: 'tahoma',
45221    
45222     fontSelect : false,
45223     
45224     
45225     formatCombo : false,
45226     
45227     init : function(editor)
45228     {
45229         this.editor = editor;
45230         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45231         var editorcore = this.editorcore;
45232         
45233         var _t = this;
45234         
45235         var fid = editorcore.frameId;
45236         var etb = this;
45237         function btn(id, toggle, handler){
45238             var xid = fid + '-'+ id ;
45239             return {
45240                 id : xid,
45241                 cmd : id,
45242                 cls : 'x-btn-icon x-edit-'+id,
45243                 enableToggle:toggle !== false,
45244                 scope: _t, // was editor...
45245                 handler:handler||_t.relayBtnCmd,
45246                 clickEvent:'mousedown',
45247                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45248                 tabIndex:-1
45249             };
45250         }
45251         
45252         
45253         
45254         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45255         this.tb = tb;
45256          // stop form submits
45257         tb.el.on('click', function(e){
45258             e.preventDefault(); // what does this do?
45259         });
45260
45261         if(!this.disable.font) { // && !Roo.isSafari){
45262             /* why no safari for fonts 
45263             editor.fontSelect = tb.el.createChild({
45264                 tag:'select',
45265                 tabIndex: -1,
45266                 cls:'x-font-select',
45267                 html: this.createFontOptions()
45268             });
45269             
45270             editor.fontSelect.on('change', function(){
45271                 var font = editor.fontSelect.dom.value;
45272                 editor.relayCmd('fontname', font);
45273                 editor.deferFocus();
45274             }, editor);
45275             
45276             tb.add(
45277                 editor.fontSelect.dom,
45278                 '-'
45279             );
45280             */
45281             
45282         };
45283         if(!this.disable.formats){
45284             this.formatCombo = new Roo.form.ComboBox({
45285                 store: new Roo.data.SimpleStore({
45286                     id : 'tag',
45287                     fields: ['tag'],
45288                     data : this.formats // from states.js
45289                 }),
45290                 blockFocus : true,
45291                 name : '',
45292                 //autoCreate : {tag: "div",  size: "20"},
45293                 displayField:'tag',
45294                 typeAhead: false,
45295                 mode: 'local',
45296                 editable : false,
45297                 triggerAction: 'all',
45298                 emptyText:'Add tag',
45299                 selectOnFocus:true,
45300                 width:135,
45301                 listeners : {
45302                     'select': function(c, r, i) {
45303                         editorcore.insertTag(r.get('tag'));
45304                         editor.focus();
45305                     }
45306                 }
45307
45308             });
45309             tb.addField(this.formatCombo);
45310             
45311         }
45312         
45313         if(!this.disable.format){
45314             tb.add(
45315                 btn('bold'),
45316                 btn('italic'),
45317                 btn('underline'),
45318                 btn('strikethrough')
45319             );
45320         };
45321         if(!this.disable.fontSize){
45322             tb.add(
45323                 '-',
45324                 
45325                 
45326                 btn('increasefontsize', false, editorcore.adjustFont),
45327                 btn('decreasefontsize', false, editorcore.adjustFont)
45328             );
45329         };
45330         
45331         
45332         if(!this.disable.colors){
45333             tb.add(
45334                 '-', {
45335                     id:editorcore.frameId +'-forecolor',
45336                     cls:'x-btn-icon x-edit-forecolor',
45337                     clickEvent:'mousedown',
45338                     tooltip: this.buttonTips['forecolor'] || undefined,
45339                     tabIndex:-1,
45340                     menu : new Roo.menu.ColorMenu({
45341                         allowReselect: true,
45342                         focus: Roo.emptyFn,
45343                         value:'000000',
45344                         plain:true,
45345                         selectHandler: function(cp, color){
45346                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
45347                             editor.deferFocus();
45348                         },
45349                         scope: editorcore,
45350                         clickEvent:'mousedown'
45351                     })
45352                 }, {
45353                     id:editorcore.frameId +'backcolor',
45354                     cls:'x-btn-icon x-edit-backcolor',
45355                     clickEvent:'mousedown',
45356                     tooltip: this.buttonTips['backcolor'] || undefined,
45357                     tabIndex:-1,
45358                     menu : new Roo.menu.ColorMenu({
45359                         focus: Roo.emptyFn,
45360                         value:'FFFFFF',
45361                         plain:true,
45362                         allowReselect: true,
45363                         selectHandler: function(cp, color){
45364                             if(Roo.isGecko){
45365                                 editorcore.execCmd('useCSS', false);
45366                                 editorcore.execCmd('hilitecolor', color);
45367                                 editorcore.execCmd('useCSS', true);
45368                                 editor.deferFocus();
45369                             }else{
45370                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
45371                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
45372                                 editor.deferFocus();
45373                             }
45374                         },
45375                         scope:editorcore,
45376                         clickEvent:'mousedown'
45377                     })
45378                 }
45379             );
45380         };
45381         // now add all the items...
45382         
45383
45384         if(!this.disable.alignments){
45385             tb.add(
45386                 '-',
45387                 btn('justifyleft'),
45388                 btn('justifycenter'),
45389                 btn('justifyright')
45390             );
45391         };
45392
45393         //if(!Roo.isSafari){
45394             if(!this.disable.links){
45395                 tb.add(
45396                     '-',
45397                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
45398                 );
45399             };
45400
45401             if(!this.disable.lists){
45402                 tb.add(
45403                     '-',
45404                     btn('insertorderedlist'),
45405                     btn('insertunorderedlist')
45406                 );
45407             }
45408             if(!this.disable.sourceEdit){
45409                 tb.add(
45410                     '-',
45411                     btn('sourceedit', true, function(btn){
45412                         this.toggleSourceEdit(btn.pressed);
45413                     })
45414                 );
45415             }
45416         //}
45417         
45418         var smenu = { };
45419         // special menu.. - needs to be tidied up..
45420         if (!this.disable.special) {
45421             smenu = {
45422                 text: "&#169;",
45423                 cls: 'x-edit-none',
45424                 
45425                 menu : {
45426                     items : []
45427                 }
45428             };
45429             for (var i =0; i < this.specialChars.length; i++) {
45430                 smenu.menu.items.push({
45431                     
45432                     html: this.specialChars[i],
45433                     handler: function(a,b) {
45434                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
45435                         //editor.insertAtCursor(a.html);
45436                         
45437                     },
45438                     tabIndex:-1
45439                 });
45440             }
45441             
45442             
45443             tb.add(smenu);
45444             
45445             
45446         }
45447         
45448         var cmenu = { };
45449         if (!this.disable.cleanStyles) {
45450             cmenu = {
45451                 cls: 'x-btn-icon x-btn-clear',
45452                 
45453                 menu : {
45454                     items : []
45455                 }
45456             };
45457             for (var i =0; i < this.cleanStyles.length; i++) {
45458                 cmenu.menu.items.push({
45459                     actiontype : this.cleanStyles[i],
45460                     html: 'Remove ' + this.cleanStyles[i],
45461                     handler: function(a,b) {
45462 //                        Roo.log(a);
45463 //                        Roo.log(b);
45464                         var c = Roo.get(editorcore.doc.body);
45465                         c.select('[style]').each(function(s) {
45466                             s.dom.style.removeProperty(a.actiontype);
45467                         });
45468                         editorcore.syncValue();
45469                     },
45470                     tabIndex:-1
45471                 });
45472             }
45473              cmenu.menu.items.push({
45474                 actiontype : 'tablewidths',
45475                 html: 'Remove Table Widths',
45476                 handler: function(a,b) {
45477                     editorcore.cleanTableWidths();
45478                     editorcore.syncValue();
45479                 },
45480                 tabIndex:-1
45481             });
45482             cmenu.menu.items.push({
45483                 actiontype : 'word',
45484                 html: 'Remove MS Word Formating',
45485                 handler: function(a,b) {
45486                     editorcore.cleanWord();
45487                     editorcore.syncValue();
45488                 },
45489                 tabIndex:-1
45490             });
45491             
45492             cmenu.menu.items.push({
45493                 actiontype : 'all',
45494                 html: 'Remove All Styles',
45495                 handler: function(a,b) {
45496                     
45497                     var c = Roo.get(editorcore.doc.body);
45498                     c.select('[style]').each(function(s) {
45499                         s.dom.removeAttribute('style');
45500                     });
45501                     editorcore.syncValue();
45502                 },
45503                 tabIndex:-1
45504             });
45505             
45506             cmenu.menu.items.push({
45507                 actiontype : 'all',
45508                 html: 'Remove All CSS Classes',
45509                 handler: function(a,b) {
45510                     
45511                     var c = Roo.get(editorcore.doc.body);
45512                     c.select('[class]').each(function(s) {
45513                         s.dom.className = '';
45514                     });
45515                     editorcore.syncValue();
45516                 },
45517                 tabIndex:-1
45518             });
45519             
45520              cmenu.menu.items.push({
45521                 actiontype : 'tidy',
45522                 html: 'Tidy HTML Source',
45523                 handler: function(a,b) {
45524                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
45525                     editorcore.syncValue();
45526                 },
45527                 tabIndex:-1
45528             });
45529             
45530             
45531             tb.add(cmenu);
45532         }
45533          
45534         if (!this.disable.specialElements) {
45535             var semenu = {
45536                 text: "Other;",
45537                 cls: 'x-edit-none',
45538                 menu : {
45539                     items : []
45540                 }
45541             };
45542             for (var i =0; i < this.specialElements.length; i++) {
45543                 semenu.menu.items.push(
45544                     Roo.apply({ 
45545                         handler: function(a,b) {
45546                             editor.insertAtCursor(this.ihtml);
45547                         }
45548                     }, this.specialElements[i])
45549                 );
45550                     
45551             }
45552             
45553             tb.add(semenu);
45554             
45555             
45556         }
45557          
45558         
45559         if (this.btns) {
45560             for(var i =0; i< this.btns.length;i++) {
45561                 var b = Roo.factory(this.btns[i],Roo.form);
45562                 b.cls =  'x-edit-none';
45563                 
45564                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
45565                     b.cls += ' x-init-enable';
45566                 }
45567                 
45568                 b.scope = editorcore;
45569                 tb.add(b);
45570             }
45571         
45572         }
45573         
45574         
45575         
45576         // disable everything...
45577         
45578         this.tb.items.each(function(item){
45579             
45580            if(
45581                 item.id != editorcore.frameId+ '-sourceedit' && 
45582                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
45583             ){
45584                 
45585                 item.disable();
45586             }
45587         });
45588         this.rendered = true;
45589         
45590         // the all the btns;
45591         editor.on('editorevent', this.updateToolbar, this);
45592         // other toolbars need to implement this..
45593         //editor.on('editmodechange', this.updateToolbar, this);
45594     },
45595     
45596     
45597     relayBtnCmd : function(btn) {
45598         this.editorcore.relayCmd(btn.cmd);
45599     },
45600     // private used internally
45601     createLink : function(){
45602         Roo.log("create link?");
45603         var url = prompt(this.createLinkText, this.defaultLinkValue);
45604         if(url && url != 'http:/'+'/'){
45605             this.editorcore.relayCmd('createlink', url);
45606         }
45607     },
45608
45609     
45610     /**
45611      * Protected method that will not generally be called directly. It triggers
45612      * a toolbar update by reading the markup state of the current selection in the editor.
45613      */
45614     updateToolbar: function(){
45615
45616         if(!this.editorcore.activated){
45617             this.editor.onFirstFocus();
45618             return;
45619         }
45620
45621         var btns = this.tb.items.map, 
45622             doc = this.editorcore.doc,
45623             frameId = this.editorcore.frameId;
45624
45625         if(!this.disable.font && !Roo.isSafari){
45626             /*
45627             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
45628             if(name != this.fontSelect.dom.value){
45629                 this.fontSelect.dom.value = name;
45630             }
45631             */
45632         }
45633         if(!this.disable.format){
45634             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
45635             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
45636             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
45637             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
45638         }
45639         if(!this.disable.alignments){
45640             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
45641             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
45642             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
45643         }
45644         if(!Roo.isSafari && !this.disable.lists){
45645             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
45646             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
45647         }
45648         
45649         var ans = this.editorcore.getAllAncestors();
45650         if (this.formatCombo) {
45651             
45652             
45653             var store = this.formatCombo.store;
45654             this.formatCombo.setValue("");
45655             for (var i =0; i < ans.length;i++) {
45656                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
45657                     // select it..
45658                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
45659                     break;
45660                 }
45661             }
45662         }
45663         
45664         
45665         
45666         // hides menus... - so this cant be on a menu...
45667         Roo.menu.MenuMgr.hideAll();
45668
45669         //this.editorsyncValue();
45670     },
45671    
45672     
45673     createFontOptions : function(){
45674         var buf = [], fs = this.fontFamilies, ff, lc;
45675         
45676         
45677         
45678         for(var i = 0, len = fs.length; i< len; i++){
45679             ff = fs[i];
45680             lc = ff.toLowerCase();
45681             buf.push(
45682                 '<option value="',lc,'" style="font-family:',ff,';"',
45683                     (this.defaultFont == lc ? ' selected="true">' : '>'),
45684                     ff,
45685                 '</option>'
45686             );
45687         }
45688         return buf.join('');
45689     },
45690     
45691     toggleSourceEdit : function(sourceEditMode){
45692         
45693         Roo.log("toolbar toogle");
45694         if(sourceEditMode === undefined){
45695             sourceEditMode = !this.sourceEditMode;
45696         }
45697         this.sourceEditMode = sourceEditMode === true;
45698         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
45699         // just toggle the button?
45700         if(btn.pressed !== this.sourceEditMode){
45701             btn.toggle(this.sourceEditMode);
45702             return;
45703         }
45704         
45705         if(sourceEditMode){
45706             Roo.log("disabling buttons");
45707             this.tb.items.each(function(item){
45708                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
45709                     item.disable();
45710                 }
45711             });
45712           
45713         }else{
45714             Roo.log("enabling buttons");
45715             if(this.editorcore.initialized){
45716                 this.tb.items.each(function(item){
45717                     item.enable();
45718                 });
45719             }
45720             
45721         }
45722         Roo.log("calling toggole on editor");
45723         // tell the editor that it's been pressed..
45724         this.editor.toggleSourceEdit(sourceEditMode);
45725        
45726     },
45727      /**
45728      * Object collection of toolbar tooltips for the buttons in the editor. The key
45729      * is the command id associated with that button and the value is a valid QuickTips object.
45730      * For example:
45731 <pre><code>
45732 {
45733     bold : {
45734         title: 'Bold (Ctrl+B)',
45735         text: 'Make the selected text bold.',
45736         cls: 'x-html-editor-tip'
45737     },
45738     italic : {
45739         title: 'Italic (Ctrl+I)',
45740         text: 'Make the selected text italic.',
45741         cls: 'x-html-editor-tip'
45742     },
45743     ...
45744 </code></pre>
45745     * @type Object
45746      */
45747     buttonTips : {
45748         bold : {
45749             title: 'Bold (Ctrl+B)',
45750             text: 'Make the selected text bold.',
45751             cls: 'x-html-editor-tip'
45752         },
45753         italic : {
45754             title: 'Italic (Ctrl+I)',
45755             text: 'Make the selected text italic.',
45756             cls: 'x-html-editor-tip'
45757         },
45758         underline : {
45759             title: 'Underline (Ctrl+U)',
45760             text: 'Underline the selected text.',
45761             cls: 'x-html-editor-tip'
45762         },
45763         strikethrough : {
45764             title: 'Strikethrough',
45765             text: 'Strikethrough the selected text.',
45766             cls: 'x-html-editor-tip'
45767         },
45768         increasefontsize : {
45769             title: 'Grow Text',
45770             text: 'Increase the font size.',
45771             cls: 'x-html-editor-tip'
45772         },
45773         decreasefontsize : {
45774             title: 'Shrink Text',
45775             text: 'Decrease the font size.',
45776             cls: 'x-html-editor-tip'
45777         },
45778         backcolor : {
45779             title: 'Text Highlight Color',
45780             text: 'Change the background color of the selected text.',
45781             cls: 'x-html-editor-tip'
45782         },
45783         forecolor : {
45784             title: 'Font Color',
45785             text: 'Change the color of the selected text.',
45786             cls: 'x-html-editor-tip'
45787         },
45788         justifyleft : {
45789             title: 'Align Text Left',
45790             text: 'Align text to the left.',
45791             cls: 'x-html-editor-tip'
45792         },
45793         justifycenter : {
45794             title: 'Center Text',
45795             text: 'Center text in the editor.',
45796             cls: 'x-html-editor-tip'
45797         },
45798         justifyright : {
45799             title: 'Align Text Right',
45800             text: 'Align text to the right.',
45801             cls: 'x-html-editor-tip'
45802         },
45803         insertunorderedlist : {
45804             title: 'Bullet List',
45805             text: 'Start a bulleted list.',
45806             cls: 'x-html-editor-tip'
45807         },
45808         insertorderedlist : {
45809             title: 'Numbered List',
45810             text: 'Start a numbered list.',
45811             cls: 'x-html-editor-tip'
45812         },
45813         createlink : {
45814             title: 'Hyperlink',
45815             text: 'Make the selected text a hyperlink.',
45816             cls: 'x-html-editor-tip'
45817         },
45818         sourceedit : {
45819             title: 'Source Edit',
45820             text: 'Switch to source editing mode.',
45821             cls: 'x-html-editor-tip'
45822         }
45823     },
45824     // private
45825     onDestroy : function(){
45826         if(this.rendered){
45827             
45828             this.tb.items.each(function(item){
45829                 if(item.menu){
45830                     item.menu.removeAll();
45831                     if(item.menu.el){
45832                         item.menu.el.destroy();
45833                     }
45834                 }
45835                 item.destroy();
45836             });
45837              
45838         }
45839     },
45840     onFirstFocus: function() {
45841         this.tb.items.each(function(item){
45842            item.enable();
45843         });
45844     }
45845 });
45846
45847
45848
45849
45850 // <script type="text/javascript">
45851 /*
45852  * Based on
45853  * Ext JS Library 1.1.1
45854  * Copyright(c) 2006-2007, Ext JS, LLC.
45855  *  
45856  
45857  */
45858
45859  
45860 /**
45861  * @class Roo.form.HtmlEditor.ToolbarContext
45862  * Context Toolbar
45863  * 
45864  * Usage:
45865  *
45866  new Roo.form.HtmlEditor({
45867     ....
45868     toolbars : [
45869         { xtype: 'ToolbarStandard', styles : {} }
45870         { xtype: 'ToolbarContext', disable : {} }
45871     ]
45872 })
45873
45874      
45875  * 
45876  * @config : {Object} disable List of elements to disable.. (not done yet.)
45877  * @config : {Object} styles  Map of styles available.
45878  * 
45879  */
45880
45881 Roo.form.HtmlEditor.ToolbarContext = function(config)
45882 {
45883     
45884     Roo.apply(this, config);
45885     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45886     // dont call parent... till later.
45887     this.styles = this.styles || {};
45888 }
45889
45890  
45891
45892 Roo.form.HtmlEditor.ToolbarContext.types = {
45893     'IMG' : {
45894         width : {
45895             title: "Width",
45896             width: 40
45897         },
45898         height:  {
45899             title: "Height",
45900             width: 40
45901         },
45902         align: {
45903             title: "Align",
45904             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
45905             width : 80
45906             
45907         },
45908         border: {
45909             title: "Border",
45910             width: 40
45911         },
45912         alt: {
45913             title: "Alt",
45914             width: 120
45915         },
45916         src : {
45917             title: "Src",
45918             width: 220
45919         }
45920         
45921     },
45922     'A' : {
45923         name : {
45924             title: "Name",
45925             width: 50
45926         },
45927         target:  {
45928             title: "Target",
45929             width: 120
45930         },
45931         href:  {
45932             title: "Href",
45933             width: 220
45934         } // border?
45935         
45936     },
45937     'TABLE' : {
45938         rows : {
45939             title: "Rows",
45940             width: 20
45941         },
45942         cols : {
45943             title: "Cols",
45944             width: 20
45945         },
45946         width : {
45947             title: "Width",
45948             width: 40
45949         },
45950         height : {
45951             title: "Height",
45952             width: 40
45953         },
45954         border : {
45955             title: "Border",
45956             width: 20
45957         }
45958     },
45959     'TD' : {
45960         width : {
45961             title: "Width",
45962             width: 40
45963         },
45964         height : {
45965             title: "Height",
45966             width: 40
45967         },   
45968         align: {
45969             title: "Align",
45970             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
45971             width: 80
45972         },
45973         valign: {
45974             title: "Valign",
45975             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
45976             width: 80
45977         },
45978         colspan: {
45979             title: "Colspan",
45980             width: 20
45981             
45982         },
45983          'font-family'  : {
45984             title : "Font",
45985             style : 'fontFamily',
45986             displayField: 'display',
45987             optname : 'font-family',
45988             width: 140
45989         }
45990     },
45991     'INPUT' : {
45992         name : {
45993             title: "name",
45994             width: 120
45995         },
45996         value : {
45997             title: "Value",
45998             width: 120
45999         },
46000         width : {
46001             title: "Width",
46002             width: 40
46003         }
46004     },
46005     'LABEL' : {
46006         'for' : {
46007             title: "For",
46008             width: 120
46009         }
46010     },
46011     'TEXTAREA' : {
46012           name : {
46013             title: "name",
46014             width: 120
46015         },
46016         rows : {
46017             title: "Rows",
46018             width: 20
46019         },
46020         cols : {
46021             title: "Cols",
46022             width: 20
46023         }
46024     },
46025     'SELECT' : {
46026         name : {
46027             title: "name",
46028             width: 120
46029         },
46030         selectoptions : {
46031             title: "Options",
46032             width: 200
46033         }
46034     },
46035     
46036     // should we really allow this??
46037     // should this just be 
46038     'BODY' : {
46039         title : {
46040             title: "Title",
46041             width: 200,
46042             disabled : true
46043         }
46044     },
46045     'SPAN' : {
46046         'font-family'  : {
46047             title : "Font",
46048             style : 'fontFamily',
46049             displayField: 'display',
46050             optname : 'font-family',
46051             width: 140
46052         }
46053     },
46054     'DIV' : {
46055         'font-family'  : {
46056             title : "Font",
46057             style : 'fontFamily',
46058             displayField: 'display',
46059             optname : 'font-family',
46060             width: 140
46061         }
46062     },
46063      'P' : {
46064         'font-family'  : {
46065             title : "Font",
46066             style : 'fontFamily',
46067             displayField: 'display',
46068             optname : 'font-family',
46069             width: 140
46070         }
46071     },
46072     
46073     '*' : {
46074         // empty..
46075     }
46076
46077 };
46078
46079 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
46080 Roo.form.HtmlEditor.ToolbarContext.stores = false;
46081
46082 Roo.form.HtmlEditor.ToolbarContext.options = {
46083         'font-family'  : [ 
46084                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
46085                 [ 'Courier New', 'Courier New'],
46086                 [ 'Tahoma', 'Tahoma'],
46087                 [ 'Times New Roman,serif', 'Times'],
46088                 [ 'Verdana','Verdana' ]
46089         ]
46090 };
46091
46092 // fixme - these need to be configurable..
46093  
46094
46095 //Roo.form.HtmlEditor.ToolbarContext.types
46096
46097
46098 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
46099     
46100     tb: false,
46101     
46102     rendered: false,
46103     
46104     editor : false,
46105     editorcore : false,
46106     /**
46107      * @cfg {Object} disable  List of toolbar elements to disable
46108          
46109      */
46110     disable : false,
46111     /**
46112      * @cfg {Object} styles List of styles 
46113      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
46114      *
46115      * These must be defined in the page, so they get rendered correctly..
46116      * .headline { }
46117      * TD.underline { }
46118      * 
46119      */
46120     styles : false,
46121     
46122     options: false,
46123     
46124     toolbars : false,
46125     
46126     init : function(editor)
46127     {
46128         this.editor = editor;
46129         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46130         var editorcore = this.editorcore;
46131         
46132         var fid = editorcore.frameId;
46133         var etb = this;
46134         function btn(id, toggle, handler){
46135             var xid = fid + '-'+ id ;
46136             return {
46137                 id : xid,
46138                 cmd : id,
46139                 cls : 'x-btn-icon x-edit-'+id,
46140                 enableToggle:toggle !== false,
46141                 scope: editorcore, // was editor...
46142                 handler:handler||editorcore.relayBtnCmd,
46143                 clickEvent:'mousedown',
46144                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46145                 tabIndex:-1
46146             };
46147         }
46148         // create a new element.
46149         var wdiv = editor.wrap.createChild({
46150                 tag: 'div'
46151             }, editor.wrap.dom.firstChild.nextSibling, true);
46152         
46153         // can we do this more than once??
46154         
46155          // stop form submits
46156       
46157  
46158         // disable everything...
46159         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46160         this.toolbars = {};
46161            
46162         for (var i in  ty) {
46163           
46164             this.toolbars[i] = this.buildToolbar(ty[i],i);
46165         }
46166         this.tb = this.toolbars.BODY;
46167         this.tb.el.show();
46168         this.buildFooter();
46169         this.footer.show();
46170         editor.on('hide', function( ) { this.footer.hide() }, this);
46171         editor.on('show', function( ) { this.footer.show() }, this);
46172         
46173          
46174         this.rendered = true;
46175         
46176         // the all the btns;
46177         editor.on('editorevent', this.updateToolbar, this);
46178         // other toolbars need to implement this..
46179         //editor.on('editmodechange', this.updateToolbar, this);
46180     },
46181     
46182     
46183     
46184     /**
46185      * Protected method that will not generally be called directly. It triggers
46186      * a toolbar update by reading the markup state of the current selection in the editor.
46187      *
46188      * Note you can force an update by calling on('editorevent', scope, false)
46189      */
46190     updateToolbar: function(editor,ev,sel){
46191
46192         //Roo.log(ev);
46193         // capture mouse up - this is handy for selecting images..
46194         // perhaps should go somewhere else...
46195         if(!this.editorcore.activated){
46196              this.editor.onFirstFocus();
46197             return;
46198         }
46199         
46200         
46201         
46202         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46203         // selectNode - might want to handle IE?
46204         if (ev &&
46205             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46206             ev.target && ev.target.tagName == 'IMG') {
46207             // they have click on an image...
46208             // let's see if we can change the selection...
46209             sel = ev.target;
46210          
46211               var nodeRange = sel.ownerDocument.createRange();
46212             try {
46213                 nodeRange.selectNode(sel);
46214             } catch (e) {
46215                 nodeRange.selectNodeContents(sel);
46216             }
46217             //nodeRange.collapse(true);
46218             var s = this.editorcore.win.getSelection();
46219             s.removeAllRanges();
46220             s.addRange(nodeRange);
46221         }  
46222         
46223       
46224         var updateFooter = sel ? false : true;
46225         
46226         
46227         var ans = this.editorcore.getAllAncestors();
46228         
46229         // pick
46230         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46231         
46232         if (!sel) { 
46233             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
46234             sel = sel ? sel : this.editorcore.doc.body;
46235             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
46236             
46237         }
46238         // pick a menu that exists..
46239         var tn = sel.tagName.toUpperCase();
46240         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
46241         
46242         tn = sel.tagName.toUpperCase();
46243         
46244         var lastSel = this.tb.selectedNode;
46245         
46246         this.tb.selectedNode = sel;
46247         
46248         // if current menu does not match..
46249         
46250         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
46251                 
46252             this.tb.el.hide();
46253             ///console.log("show: " + tn);
46254             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
46255             this.tb.el.show();
46256             // update name
46257             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
46258             
46259             
46260             // update attributes
46261             if (this.tb.fields) {
46262                 this.tb.fields.each(function(e) {
46263                     if (e.stylename) {
46264                         e.setValue(sel.style[e.stylename]);
46265                         return;
46266                     } 
46267                    e.setValue(sel.getAttribute(e.attrname));
46268                 });
46269             }
46270             
46271             var hasStyles = false;
46272             for(var i in this.styles) {
46273                 hasStyles = true;
46274                 break;
46275             }
46276             
46277             // update styles
46278             if (hasStyles) { 
46279                 var st = this.tb.fields.item(0);
46280                 
46281                 st.store.removeAll();
46282                
46283                 
46284                 var cn = sel.className.split(/\s+/);
46285                 
46286                 var avs = [];
46287                 if (this.styles['*']) {
46288                     
46289                     Roo.each(this.styles['*'], function(v) {
46290                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46291                     });
46292                 }
46293                 if (this.styles[tn]) { 
46294                     Roo.each(this.styles[tn], function(v) {
46295                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46296                     });
46297                 }
46298                 
46299                 st.store.loadData(avs);
46300                 st.collapse();
46301                 st.setValue(cn);
46302             }
46303             // flag our selected Node.
46304             this.tb.selectedNode = sel;
46305            
46306            
46307             Roo.menu.MenuMgr.hideAll();
46308
46309         }
46310         
46311         if (!updateFooter) {
46312             //this.footDisp.dom.innerHTML = ''; 
46313             return;
46314         }
46315         // update the footer
46316         //
46317         var html = '';
46318         
46319         this.footerEls = ans.reverse();
46320         Roo.each(this.footerEls, function(a,i) {
46321             if (!a) { return; }
46322             html += html.length ? ' &gt; '  :  '';
46323             
46324             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
46325             
46326         });
46327        
46328         // 
46329         var sz = this.footDisp.up('td').getSize();
46330         this.footDisp.dom.style.width = (sz.width -10) + 'px';
46331         this.footDisp.dom.style.marginLeft = '5px';
46332         
46333         this.footDisp.dom.style.overflow = 'hidden';
46334         
46335         this.footDisp.dom.innerHTML = html;
46336             
46337         //this.editorsyncValue();
46338     },
46339      
46340     
46341    
46342        
46343     // private
46344     onDestroy : function(){
46345         if(this.rendered){
46346             
46347             this.tb.items.each(function(item){
46348                 if(item.menu){
46349                     item.menu.removeAll();
46350                     if(item.menu.el){
46351                         item.menu.el.destroy();
46352                     }
46353                 }
46354                 item.destroy();
46355             });
46356              
46357         }
46358     },
46359     onFirstFocus: function() {
46360         // need to do this for all the toolbars..
46361         this.tb.items.each(function(item){
46362            item.enable();
46363         });
46364     },
46365     buildToolbar: function(tlist, nm)
46366     {
46367         var editor = this.editor;
46368         var editorcore = this.editorcore;
46369          // create a new element.
46370         var wdiv = editor.wrap.createChild({
46371                 tag: 'div'
46372             }, editor.wrap.dom.firstChild.nextSibling, true);
46373         
46374        
46375         var tb = new Roo.Toolbar(wdiv);
46376         // add the name..
46377         
46378         tb.add(nm+ ":&nbsp;");
46379         
46380         var styles = [];
46381         for(var i in this.styles) {
46382             styles.push(i);
46383         }
46384         
46385         // styles...
46386         if (styles && styles.length) {
46387             
46388             // this needs a multi-select checkbox...
46389             tb.addField( new Roo.form.ComboBox({
46390                 store: new Roo.data.SimpleStore({
46391                     id : 'val',
46392                     fields: ['val', 'selected'],
46393                     data : [] 
46394                 }),
46395                 name : '-roo-edit-className',
46396                 attrname : 'className',
46397                 displayField: 'val',
46398                 typeAhead: false,
46399                 mode: 'local',
46400                 editable : false,
46401                 triggerAction: 'all',
46402                 emptyText:'Select Style',
46403                 selectOnFocus:true,
46404                 width: 130,
46405                 listeners : {
46406                     'select': function(c, r, i) {
46407                         // initial support only for on class per el..
46408                         tb.selectedNode.className =  r ? r.get('val') : '';
46409                         editorcore.syncValue();
46410                     }
46411                 }
46412     
46413             }));
46414         }
46415         
46416         var tbc = Roo.form.HtmlEditor.ToolbarContext;
46417         var tbops = tbc.options;
46418         
46419         for (var i in tlist) {
46420             
46421             var item = tlist[i];
46422             tb.add(item.title + ":&nbsp;");
46423             
46424             
46425             //optname == used so you can configure the options available..
46426             var opts = item.opts ? item.opts : false;
46427             if (item.optname) {
46428                 opts = tbops[item.optname];
46429            
46430             }
46431             
46432             if (opts) {
46433                 // opts == pulldown..
46434                 tb.addField( new Roo.form.ComboBox({
46435                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
46436                         id : 'val',
46437                         fields: ['val', 'display'],
46438                         data : opts  
46439                     }),
46440                     name : '-roo-edit-' + i,
46441                     attrname : i,
46442                     stylename : item.style ? item.style : false,
46443                     displayField: item.displayField ? item.displayField : 'val',
46444                     valueField :  'val',
46445                     typeAhead: false,
46446                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
46447                     editable : false,
46448                     triggerAction: 'all',
46449                     emptyText:'Select',
46450                     selectOnFocus:true,
46451                     width: item.width ? item.width  : 130,
46452                     listeners : {
46453                         'select': function(c, r, i) {
46454                             if (c.stylename) {
46455                                 tb.selectedNode.style[c.stylename] =  r.get('val');
46456                                 return;
46457                             }
46458                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
46459                         }
46460                     }
46461
46462                 }));
46463                 continue;
46464                     
46465                  
46466                 
46467                 tb.addField( new Roo.form.TextField({
46468                     name: i,
46469                     width: 100,
46470                     //allowBlank:false,
46471                     value: ''
46472                 }));
46473                 continue;
46474             }
46475             tb.addField( new Roo.form.TextField({
46476                 name: '-roo-edit-' + i,
46477                 attrname : i,
46478                 
46479                 width: item.width,
46480                 //allowBlank:true,
46481                 value: '',
46482                 listeners: {
46483                     'change' : function(f, nv, ov) {
46484                         tb.selectedNode.setAttribute(f.attrname, nv);
46485                         editorcore.syncValue();
46486                     }
46487                 }
46488             }));
46489              
46490         }
46491         
46492         var _this = this;
46493         
46494         if(nm == 'BODY'){
46495             tb.addSeparator();
46496         
46497             tb.addButton( {
46498                 text: 'Stylesheets',
46499
46500                 listeners : {
46501                     click : function ()
46502                     {
46503                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
46504                     }
46505                 }
46506             });
46507         }
46508         
46509         tb.addFill();
46510         tb.addButton( {
46511             text: 'Remove Tag',
46512     
46513             listeners : {
46514                 click : function ()
46515                 {
46516                     // remove
46517                     // undo does not work.
46518                      
46519                     var sn = tb.selectedNode;
46520                     
46521                     var pn = sn.parentNode;
46522                     
46523                     var stn =  sn.childNodes[0];
46524                     var en = sn.childNodes[sn.childNodes.length - 1 ];
46525                     while (sn.childNodes.length) {
46526                         var node = sn.childNodes[0];
46527                         sn.removeChild(node);
46528                         //Roo.log(node);
46529                         pn.insertBefore(node, sn);
46530                         
46531                     }
46532                     pn.removeChild(sn);
46533                     var range = editorcore.createRange();
46534         
46535                     range.setStart(stn,0);
46536                     range.setEnd(en,0); //????
46537                     //range.selectNode(sel);
46538                     
46539                     
46540                     var selection = editorcore.getSelection();
46541                     selection.removeAllRanges();
46542                     selection.addRange(range);
46543                     
46544                     
46545                     
46546                     //_this.updateToolbar(null, null, pn);
46547                     _this.updateToolbar(null, null, null);
46548                     _this.footDisp.dom.innerHTML = ''; 
46549                 }
46550             }
46551             
46552                     
46553                 
46554             
46555         });
46556         
46557         
46558         tb.el.on('click', function(e){
46559             e.preventDefault(); // what does this do?
46560         });
46561         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
46562         tb.el.hide();
46563         tb.name = nm;
46564         // dont need to disable them... as they will get hidden
46565         return tb;
46566          
46567         
46568     },
46569     buildFooter : function()
46570     {
46571         
46572         var fel = this.editor.wrap.createChild();
46573         this.footer = new Roo.Toolbar(fel);
46574         // toolbar has scrolly on left / right?
46575         var footDisp= new Roo.Toolbar.Fill();
46576         var _t = this;
46577         this.footer.add(
46578             {
46579                 text : '&lt;',
46580                 xtype: 'Button',
46581                 handler : function() {
46582                     _t.footDisp.scrollTo('left',0,true)
46583                 }
46584             }
46585         );
46586         this.footer.add( footDisp );
46587         this.footer.add( 
46588             {
46589                 text : '&gt;',
46590                 xtype: 'Button',
46591                 handler : function() {
46592                     // no animation..
46593                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
46594                 }
46595             }
46596         );
46597         var fel = Roo.get(footDisp.el);
46598         fel.addClass('x-editor-context');
46599         this.footDispWrap = fel; 
46600         this.footDispWrap.overflow  = 'hidden';
46601         
46602         this.footDisp = fel.createChild();
46603         this.footDispWrap.on('click', this.onContextClick, this)
46604         
46605         
46606     },
46607     onContextClick : function (ev,dom)
46608     {
46609         ev.preventDefault();
46610         var  cn = dom.className;
46611         //Roo.log(cn);
46612         if (!cn.match(/x-ed-loc-/)) {
46613             return;
46614         }
46615         var n = cn.split('-').pop();
46616         var ans = this.footerEls;
46617         var sel = ans[n];
46618         
46619          // pick
46620         var range = this.editorcore.createRange();
46621         
46622         range.selectNodeContents(sel);
46623         //range.selectNode(sel);
46624         
46625         
46626         var selection = this.editorcore.getSelection();
46627         selection.removeAllRanges();
46628         selection.addRange(range);
46629         
46630         
46631         
46632         this.updateToolbar(null, null, sel);
46633         
46634         
46635     }
46636     
46637     
46638     
46639     
46640     
46641 });
46642
46643
46644
46645
46646
46647 /*
46648  * Based on:
46649  * Ext JS Library 1.1.1
46650  * Copyright(c) 2006-2007, Ext JS, LLC.
46651  *
46652  * Originally Released Under LGPL - original licence link has changed is not relivant.
46653  *
46654  * Fork - LGPL
46655  * <script type="text/javascript">
46656  */
46657  
46658 /**
46659  * @class Roo.form.BasicForm
46660  * @extends Roo.util.Observable
46661  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
46662  * @constructor
46663  * @param {String/HTMLElement/Roo.Element} el The form element or its id
46664  * @param {Object} config Configuration options
46665  */
46666 Roo.form.BasicForm = function(el, config){
46667     this.allItems = [];
46668     this.childForms = [];
46669     Roo.apply(this, config);
46670     /*
46671      * The Roo.form.Field items in this form.
46672      * @type MixedCollection
46673      */
46674      
46675      
46676     this.items = new Roo.util.MixedCollection(false, function(o){
46677         return o.id || (o.id = Roo.id());
46678     });
46679     this.addEvents({
46680         /**
46681          * @event beforeaction
46682          * Fires before any action is performed. Return false to cancel the action.
46683          * @param {Form} this
46684          * @param {Action} action The action to be performed
46685          */
46686         beforeaction: true,
46687         /**
46688          * @event actionfailed
46689          * Fires when an action fails.
46690          * @param {Form} this
46691          * @param {Action} action The action that failed
46692          */
46693         actionfailed : true,
46694         /**
46695          * @event actioncomplete
46696          * Fires when an action is completed.
46697          * @param {Form} this
46698          * @param {Action} action The action that completed
46699          */
46700         actioncomplete : true
46701     });
46702     if(el){
46703         this.initEl(el);
46704     }
46705     Roo.form.BasicForm.superclass.constructor.call(this);
46706 };
46707
46708 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
46709     /**
46710      * @cfg {String} method
46711      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
46712      */
46713     /**
46714      * @cfg {DataReader} reader
46715      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
46716      * This is optional as there is built-in support for processing JSON.
46717      */
46718     /**
46719      * @cfg {DataReader} errorReader
46720      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
46721      * This is completely optional as there is built-in support for processing JSON.
46722      */
46723     /**
46724      * @cfg {String} url
46725      * The URL to use for form actions if one isn't supplied in the action options.
46726      */
46727     /**
46728      * @cfg {Boolean} fileUpload
46729      * Set to true if this form is a file upload.
46730      */
46731      
46732     /**
46733      * @cfg {Object} baseParams
46734      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
46735      */
46736      /**
46737      
46738     /**
46739      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
46740      */
46741     timeout: 30,
46742
46743     // private
46744     activeAction : null,
46745
46746     /**
46747      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
46748      * or setValues() data instead of when the form was first created.
46749      */
46750     trackResetOnLoad : false,
46751     
46752     
46753     /**
46754      * childForms - used for multi-tab forms
46755      * @type {Array}
46756      */
46757     childForms : false,
46758     
46759     /**
46760      * allItems - full list of fields.
46761      * @type {Array}
46762      */
46763     allItems : false,
46764     
46765     /**
46766      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
46767      * element by passing it or its id or mask the form itself by passing in true.
46768      * @type Mixed
46769      */
46770     waitMsgTarget : false,
46771
46772     // private
46773     initEl : function(el){
46774         this.el = Roo.get(el);
46775         this.id = this.el.id || Roo.id();
46776         this.el.on('submit', this.onSubmit, this);
46777         this.el.addClass('x-form');
46778     },
46779
46780     // private
46781     onSubmit : function(e){
46782         e.stopEvent();
46783     },
46784
46785     /**
46786      * Returns true if client-side validation on the form is successful.
46787      * @return Boolean
46788      */
46789     isValid : function(){
46790         var valid = true;
46791         this.items.each(function(f){
46792            if(!f.validate()){
46793                valid = false;
46794            }
46795         });
46796         return valid;
46797     },
46798
46799     /**
46800      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
46801      * @return Boolean
46802      */
46803     isDirty : function(){
46804         var dirty = false;
46805         this.items.each(function(f){
46806            if(f.isDirty()){
46807                dirty = true;
46808                return false;
46809            }
46810         });
46811         return dirty;
46812     },
46813     
46814     /**
46815      * Returns true if any fields in this form have changed since their original load. (New version)
46816      * @return Boolean
46817      */
46818     
46819     hasChanged : function()
46820     {
46821         var dirty = false;
46822         this.items.each(function(f){
46823            if(f.hasChanged()){
46824                dirty = true;
46825                return false;
46826            }
46827         });
46828         return dirty;
46829         
46830     },
46831     /**
46832      * Resets all hasChanged to 'false' -
46833      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
46834      * So hasChanged storage is only to be used for this purpose
46835      * @return Boolean
46836      */
46837     resetHasChanged : function()
46838     {
46839         this.items.each(function(f){
46840            f.resetHasChanged();
46841         });
46842         
46843     },
46844     
46845     
46846     /**
46847      * Performs a predefined action (submit or load) or custom actions you define on this form.
46848      * @param {String} actionName The name of the action type
46849      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
46850      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
46851      * accept other config options):
46852      * <pre>
46853 Property          Type             Description
46854 ----------------  ---------------  ----------------------------------------------------------------------------------
46855 url               String           The url for the action (defaults to the form's url)
46856 method            String           The form method to use (defaults to the form's method, or POST if not defined)
46857 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
46858 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
46859                                    validate the form on the client (defaults to false)
46860      * </pre>
46861      * @return {BasicForm} this
46862      */
46863     doAction : function(action, options){
46864         if(typeof action == 'string'){
46865             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
46866         }
46867         if(this.fireEvent('beforeaction', this, action) !== false){
46868             this.beforeAction(action);
46869             action.run.defer(100, action);
46870         }
46871         return this;
46872     },
46873
46874     /**
46875      * Shortcut to do a submit action.
46876      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46877      * @return {BasicForm} this
46878      */
46879     submit : function(options){
46880         this.doAction('submit', options);
46881         return this;
46882     },
46883
46884     /**
46885      * Shortcut to do a load action.
46886      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46887      * @return {BasicForm} this
46888      */
46889     load : function(options){
46890         this.doAction('load', options);
46891         return this;
46892     },
46893
46894     /**
46895      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
46896      * @param {Record} record The record to edit
46897      * @return {BasicForm} this
46898      */
46899     updateRecord : function(record){
46900         record.beginEdit();
46901         var fs = record.fields;
46902         fs.each(function(f){
46903             var field = this.findField(f.name);
46904             if(field){
46905                 record.set(f.name, field.getValue());
46906             }
46907         }, this);
46908         record.endEdit();
46909         return this;
46910     },
46911
46912     /**
46913      * Loads an Roo.data.Record into this form.
46914      * @param {Record} record The record to load
46915      * @return {BasicForm} this
46916      */
46917     loadRecord : function(record){
46918         this.setValues(record.data);
46919         return this;
46920     },
46921
46922     // private
46923     beforeAction : function(action){
46924         var o = action.options;
46925         
46926        
46927         if(this.waitMsgTarget === true){
46928             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
46929         }else if(this.waitMsgTarget){
46930             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
46931             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
46932         }else {
46933             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
46934         }
46935          
46936     },
46937
46938     // private
46939     afterAction : function(action, success){
46940         this.activeAction = null;
46941         var o = action.options;
46942         
46943         if(this.waitMsgTarget === true){
46944             this.el.unmask();
46945         }else if(this.waitMsgTarget){
46946             this.waitMsgTarget.unmask();
46947         }else{
46948             Roo.MessageBox.updateProgress(1);
46949             Roo.MessageBox.hide();
46950         }
46951          
46952         if(success){
46953             if(o.reset){
46954                 this.reset();
46955             }
46956             Roo.callback(o.success, o.scope, [this, action]);
46957             this.fireEvent('actioncomplete', this, action);
46958             
46959         }else{
46960             
46961             // failure condition..
46962             // we have a scenario where updates need confirming.
46963             // eg. if a locking scenario exists..
46964             // we look for { errors : { needs_confirm : true }} in the response.
46965             if (
46966                 (typeof(action.result) != 'undefined')  &&
46967                 (typeof(action.result.errors) != 'undefined')  &&
46968                 (typeof(action.result.errors.needs_confirm) != 'undefined')
46969            ){
46970                 var _t = this;
46971                 Roo.MessageBox.confirm(
46972                     "Change requires confirmation",
46973                     action.result.errorMsg,
46974                     function(r) {
46975                         if (r != 'yes') {
46976                             return;
46977                         }
46978                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
46979                     }
46980                     
46981                 );
46982                 
46983                 
46984                 
46985                 return;
46986             }
46987             
46988             Roo.callback(o.failure, o.scope, [this, action]);
46989             // show an error message if no failed handler is set..
46990             if (!this.hasListener('actionfailed')) {
46991                 Roo.MessageBox.alert("Error",
46992                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
46993                         action.result.errorMsg :
46994                         "Saving Failed, please check your entries or try again"
46995                 );
46996             }
46997             
46998             this.fireEvent('actionfailed', this, action);
46999         }
47000         
47001     },
47002
47003     /**
47004      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
47005      * @param {String} id The value to search for
47006      * @return Field
47007      */
47008     findField : function(id){
47009         var field = this.items.get(id);
47010         if(!field){
47011             this.items.each(function(f){
47012                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
47013                     field = f;
47014                     return false;
47015                 }
47016             });
47017         }
47018         return field || null;
47019     },
47020
47021     /**
47022      * Add a secondary form to this one, 
47023      * Used to provide tabbed forms. One form is primary, with hidden values 
47024      * which mirror the elements from the other forms.
47025      * 
47026      * @param {Roo.form.Form} form to add.
47027      * 
47028      */
47029     addForm : function(form)
47030     {
47031        
47032         if (this.childForms.indexOf(form) > -1) {
47033             // already added..
47034             return;
47035         }
47036         this.childForms.push(form);
47037         var n = '';
47038         Roo.each(form.allItems, function (fe) {
47039             
47040             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
47041             if (this.findField(n)) { // already added..
47042                 return;
47043             }
47044             var add = new Roo.form.Hidden({
47045                 name : n
47046             });
47047             add.render(this.el);
47048             
47049             this.add( add );
47050         }, this);
47051         
47052     },
47053     /**
47054      * Mark fields in this form invalid in bulk.
47055      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
47056      * @return {BasicForm} this
47057      */
47058     markInvalid : function(errors){
47059         if(errors instanceof Array){
47060             for(var i = 0, len = errors.length; i < len; i++){
47061                 var fieldError = errors[i];
47062                 var f = this.findField(fieldError.id);
47063                 if(f){
47064                     f.markInvalid(fieldError.msg);
47065                 }
47066             }
47067         }else{
47068             var field, id;
47069             for(id in errors){
47070                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
47071                     field.markInvalid(errors[id]);
47072                 }
47073             }
47074         }
47075         Roo.each(this.childForms || [], function (f) {
47076             f.markInvalid(errors);
47077         });
47078         
47079         return this;
47080     },
47081
47082     /**
47083      * Set values for fields in this form in bulk.
47084      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
47085      * @return {BasicForm} this
47086      */
47087     setValues : function(values){
47088         if(values instanceof Array){ // array of objects
47089             for(var i = 0, len = values.length; i < len; i++){
47090                 var v = values[i];
47091                 var f = this.findField(v.id);
47092                 if(f){
47093                     f.setValue(v.value);
47094                     if(this.trackResetOnLoad){
47095                         f.originalValue = f.getValue();
47096                     }
47097                 }
47098             }
47099         }else{ // object hash
47100             var field, id;
47101             for(id in values){
47102                 if(typeof values[id] != 'function' && (field = this.findField(id))){
47103                     
47104                     if (field.setFromData && 
47105                         field.valueField && 
47106                         field.displayField &&
47107                         // combos' with local stores can 
47108                         // be queried via setValue()
47109                         // to set their value..
47110                         (field.store && !field.store.isLocal)
47111                         ) {
47112                         // it's a combo
47113                         var sd = { };
47114                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
47115                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
47116                         field.setFromData(sd);
47117                         
47118                     } else {
47119                         field.setValue(values[id]);
47120                     }
47121                     
47122                     
47123                     if(this.trackResetOnLoad){
47124                         field.originalValue = field.getValue();
47125                     }
47126                 }
47127             }
47128         }
47129         this.resetHasChanged();
47130         
47131         
47132         Roo.each(this.childForms || [], function (f) {
47133             f.setValues(values);
47134             f.resetHasChanged();
47135         });
47136                 
47137         return this;
47138     },
47139
47140     /**
47141      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
47142      * they are returned as an array.
47143      * @param {Boolean} asString
47144      * @return {Object}
47145      */
47146     getValues : function(asString){
47147         if (this.childForms) {
47148             // copy values from the child forms
47149             Roo.each(this.childForms, function (f) {
47150                 this.setValues(f.getValues());
47151             }, this);
47152         }
47153         
47154         
47155         
47156         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47157         if(asString === true){
47158             return fs;
47159         }
47160         return Roo.urlDecode(fs);
47161     },
47162     
47163     /**
47164      * Returns the fields in this form as an object with key/value pairs. 
47165      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
47166      * @return {Object}
47167      */
47168     getFieldValues : function(with_hidden)
47169     {
47170         if (this.childForms) {
47171             // copy values from the child forms
47172             // should this call getFieldValues - probably not as we do not currently copy
47173             // hidden fields when we generate..
47174             Roo.each(this.childForms, function (f) {
47175                 this.setValues(f.getValues());
47176             }, this);
47177         }
47178         
47179         var ret = {};
47180         this.items.each(function(f){
47181             if (!f.getName()) {
47182                 return;
47183             }
47184             var v = f.getValue();
47185             if (f.inputType =='radio') {
47186                 if (typeof(ret[f.getName()]) == 'undefined') {
47187                     ret[f.getName()] = ''; // empty..
47188                 }
47189                 
47190                 if (!f.el.dom.checked) {
47191                     return;
47192                     
47193                 }
47194                 v = f.el.dom.value;
47195                 
47196             }
47197             
47198             // not sure if this supported any more..
47199             if ((typeof(v) == 'object') && f.getRawValue) {
47200                 v = f.getRawValue() ; // dates..
47201             }
47202             // combo boxes where name != hiddenName...
47203             if (f.name != f.getName()) {
47204                 ret[f.name] = f.getRawValue();
47205             }
47206             ret[f.getName()] = v;
47207         });
47208         
47209         return ret;
47210     },
47211
47212     /**
47213      * Clears all invalid messages in this form.
47214      * @return {BasicForm} this
47215      */
47216     clearInvalid : function(){
47217         this.items.each(function(f){
47218            f.clearInvalid();
47219         });
47220         
47221         Roo.each(this.childForms || [], function (f) {
47222             f.clearInvalid();
47223         });
47224         
47225         
47226         return this;
47227     },
47228
47229     /**
47230      * Resets this form.
47231      * @return {BasicForm} this
47232      */
47233     reset : function(){
47234         this.items.each(function(f){
47235             f.reset();
47236         });
47237         
47238         Roo.each(this.childForms || [], function (f) {
47239             f.reset();
47240         });
47241         this.resetHasChanged();
47242         
47243         return this;
47244     },
47245
47246     /**
47247      * Add Roo.form components to this form.
47248      * @param {Field} field1
47249      * @param {Field} field2 (optional)
47250      * @param {Field} etc (optional)
47251      * @return {BasicForm} this
47252      */
47253     add : function(){
47254         this.items.addAll(Array.prototype.slice.call(arguments, 0));
47255         return this;
47256     },
47257
47258
47259     /**
47260      * Removes a field from the items collection (does NOT remove its markup).
47261      * @param {Field} field
47262      * @return {BasicForm} this
47263      */
47264     remove : function(field){
47265         this.items.remove(field);
47266         return this;
47267     },
47268
47269     /**
47270      * Looks at the fields in this form, checks them for an id attribute,
47271      * and calls applyTo on the existing dom element with that id.
47272      * @return {BasicForm} this
47273      */
47274     render : function(){
47275         this.items.each(function(f){
47276             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
47277                 f.applyTo(f.id);
47278             }
47279         });
47280         return this;
47281     },
47282
47283     /**
47284      * Calls {@link Ext#apply} for all fields in this form with the passed object.
47285      * @param {Object} values
47286      * @return {BasicForm} this
47287      */
47288     applyToFields : function(o){
47289         this.items.each(function(f){
47290            Roo.apply(f, o);
47291         });
47292         return this;
47293     },
47294
47295     /**
47296      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
47297      * @param {Object} values
47298      * @return {BasicForm} this
47299      */
47300     applyIfToFields : function(o){
47301         this.items.each(function(f){
47302            Roo.applyIf(f, o);
47303         });
47304         return this;
47305     }
47306 });
47307
47308 // back compat
47309 Roo.BasicForm = Roo.form.BasicForm;/*
47310  * Based on:
47311  * Ext JS Library 1.1.1
47312  * Copyright(c) 2006-2007, Ext JS, LLC.
47313  *
47314  * Originally Released Under LGPL - original licence link has changed is not relivant.
47315  *
47316  * Fork - LGPL
47317  * <script type="text/javascript">
47318  */
47319
47320 /**
47321  * @class Roo.form.Form
47322  * @extends Roo.form.BasicForm
47323  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
47324  * @constructor
47325  * @param {Object} config Configuration options
47326  */
47327 Roo.form.Form = function(config){
47328     var xitems =  [];
47329     if (config.items) {
47330         xitems = config.items;
47331         delete config.items;
47332     }
47333    
47334     
47335     Roo.form.Form.superclass.constructor.call(this, null, config);
47336     this.url = this.url || this.action;
47337     if(!this.root){
47338         this.root = new Roo.form.Layout(Roo.applyIf({
47339             id: Roo.id()
47340         }, config));
47341     }
47342     this.active = this.root;
47343     /**
47344      * Array of all the buttons that have been added to this form via {@link addButton}
47345      * @type Array
47346      */
47347     this.buttons = [];
47348     this.allItems = [];
47349     this.addEvents({
47350         /**
47351          * @event clientvalidation
47352          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
47353          * @param {Form} this
47354          * @param {Boolean} valid true if the form has passed client-side validation
47355          */
47356         clientvalidation: true,
47357         /**
47358          * @event rendered
47359          * Fires when the form is rendered
47360          * @param {Roo.form.Form} form
47361          */
47362         rendered : true
47363     });
47364     
47365     if (this.progressUrl) {
47366             // push a hidden field onto the list of fields..
47367             this.addxtype( {
47368                     xns: Roo.form, 
47369                     xtype : 'Hidden', 
47370                     name : 'UPLOAD_IDENTIFIER' 
47371             });
47372         }
47373         
47374     
47375     Roo.each(xitems, this.addxtype, this);
47376     
47377     
47378     
47379 };
47380
47381 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
47382     /**
47383      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
47384      */
47385     /**
47386      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
47387      */
47388     /**
47389      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
47390      */
47391     buttonAlign:'center',
47392
47393     /**
47394      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
47395      */
47396     minButtonWidth:75,
47397
47398     /**
47399      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
47400      * This property cascades to child containers if not set.
47401      */
47402     labelAlign:'left',
47403
47404     /**
47405      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
47406      * fires a looping event with that state. This is required to bind buttons to the valid
47407      * state using the config value formBind:true on the button.
47408      */
47409     monitorValid : false,
47410
47411     /**
47412      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
47413      */
47414     monitorPoll : 200,
47415     
47416     /**
47417      * @cfg {String} progressUrl - Url to return progress data 
47418      */
47419     
47420     progressUrl : false,
47421   
47422     /**
47423      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
47424      * fields are added and the column is closed. If no fields are passed the column remains open
47425      * until end() is called.
47426      * @param {Object} config The config to pass to the column
47427      * @param {Field} field1 (optional)
47428      * @param {Field} field2 (optional)
47429      * @param {Field} etc (optional)
47430      * @return Column The column container object
47431      */
47432     column : function(c){
47433         var col = new Roo.form.Column(c);
47434         this.start(col);
47435         if(arguments.length > 1){ // duplicate code required because of Opera
47436             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47437             this.end();
47438         }
47439         return col;
47440     },
47441
47442     /**
47443      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
47444      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
47445      * until end() is called.
47446      * @param {Object} config The config to pass to the fieldset
47447      * @param {Field} field1 (optional)
47448      * @param {Field} field2 (optional)
47449      * @param {Field} etc (optional)
47450      * @return FieldSet The fieldset container object
47451      */
47452     fieldset : function(c){
47453         var fs = new Roo.form.FieldSet(c);
47454         this.start(fs);
47455         if(arguments.length > 1){ // duplicate code required because of Opera
47456             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47457             this.end();
47458         }
47459         return fs;
47460     },
47461
47462     /**
47463      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
47464      * fields are added and the container is closed. If no fields are passed the container remains open
47465      * until end() is called.
47466      * @param {Object} config The config to pass to the Layout
47467      * @param {Field} field1 (optional)
47468      * @param {Field} field2 (optional)
47469      * @param {Field} etc (optional)
47470      * @return Layout The container object
47471      */
47472     container : function(c){
47473         var l = new Roo.form.Layout(c);
47474         this.start(l);
47475         if(arguments.length > 1){ // duplicate code required because of Opera
47476             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47477             this.end();
47478         }
47479         return l;
47480     },
47481
47482     /**
47483      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
47484      * @param {Object} container A Roo.form.Layout or subclass of Layout
47485      * @return {Form} this
47486      */
47487     start : function(c){
47488         // cascade label info
47489         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
47490         this.active.stack.push(c);
47491         c.ownerCt = this.active;
47492         this.active = c;
47493         return this;
47494     },
47495
47496     /**
47497      * Closes the current open container
47498      * @return {Form} this
47499      */
47500     end : function(){
47501         if(this.active == this.root){
47502             return this;
47503         }
47504         this.active = this.active.ownerCt;
47505         return this;
47506     },
47507
47508     /**
47509      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
47510      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
47511      * as the label of the field.
47512      * @param {Field} field1
47513      * @param {Field} field2 (optional)
47514      * @param {Field} etc. (optional)
47515      * @return {Form} this
47516      */
47517     add : function(){
47518         this.active.stack.push.apply(this.active.stack, arguments);
47519         this.allItems.push.apply(this.allItems,arguments);
47520         var r = [];
47521         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
47522             if(a[i].isFormField){
47523                 r.push(a[i]);
47524             }
47525         }
47526         if(r.length > 0){
47527             Roo.form.Form.superclass.add.apply(this, r);
47528         }
47529         return this;
47530     },
47531     
47532
47533     
47534     
47535     
47536      /**
47537      * Find any element that has been added to a form, using it's ID or name
47538      * This can include framesets, columns etc. along with regular fields..
47539      * @param {String} id - id or name to find.
47540      
47541      * @return {Element} e - or false if nothing found.
47542      */
47543     findbyId : function(id)
47544     {
47545         var ret = false;
47546         if (!id) {
47547             return ret;
47548         }
47549         Roo.each(this.allItems, function(f){
47550             if (f.id == id || f.name == id ){
47551                 ret = f;
47552                 return false;
47553             }
47554         });
47555         return ret;
47556     },
47557
47558     
47559     
47560     /**
47561      * Render this form into the passed container. This should only be called once!
47562      * @param {String/HTMLElement/Element} container The element this component should be rendered into
47563      * @return {Form} this
47564      */
47565     render : function(ct)
47566     {
47567         
47568         
47569         
47570         ct = Roo.get(ct);
47571         var o = this.autoCreate || {
47572             tag: 'form',
47573             method : this.method || 'POST',
47574             id : this.id || Roo.id()
47575         };
47576         this.initEl(ct.createChild(o));
47577
47578         this.root.render(this.el);
47579         
47580        
47581              
47582         this.items.each(function(f){
47583             f.render('x-form-el-'+f.id);
47584         });
47585
47586         if(this.buttons.length > 0){
47587             // tables are required to maintain order and for correct IE layout
47588             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
47589                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
47590                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
47591             }}, null, true);
47592             var tr = tb.getElementsByTagName('tr')[0];
47593             for(var i = 0, len = this.buttons.length; i < len; i++) {
47594                 var b = this.buttons[i];
47595                 var td = document.createElement('td');
47596                 td.className = 'x-form-btn-td';
47597                 b.render(tr.appendChild(td));
47598             }
47599         }
47600         if(this.monitorValid){ // initialize after render
47601             this.startMonitoring();
47602         }
47603         this.fireEvent('rendered', this);
47604         return this;
47605     },
47606
47607     /**
47608      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
47609      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
47610      * object or a valid Roo.DomHelper element config
47611      * @param {Function} handler The function called when the button is clicked
47612      * @param {Object} scope (optional) The scope of the handler function
47613      * @return {Roo.Button}
47614      */
47615     addButton : function(config, handler, scope){
47616         var bc = {
47617             handler: handler,
47618             scope: scope,
47619             minWidth: this.minButtonWidth,
47620             hideParent:true
47621         };
47622         if(typeof config == "string"){
47623             bc.text = config;
47624         }else{
47625             Roo.apply(bc, config);
47626         }
47627         var btn = new Roo.Button(null, bc);
47628         this.buttons.push(btn);
47629         return btn;
47630     },
47631
47632      /**
47633      * Adds a series of form elements (using the xtype property as the factory method.
47634      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
47635      * @param {Object} config 
47636      */
47637     
47638     addxtype : function()
47639     {
47640         var ar = Array.prototype.slice.call(arguments, 0);
47641         var ret = false;
47642         for(var i = 0; i < ar.length; i++) {
47643             if (!ar[i]) {
47644                 continue; // skip -- if this happends something invalid got sent, we 
47645                 // should ignore it, as basically that interface element will not show up
47646                 // and that should be pretty obvious!!
47647             }
47648             
47649             if (Roo.form[ar[i].xtype]) {
47650                 ar[i].form = this;
47651                 var fe = Roo.factory(ar[i], Roo.form);
47652                 if (!ret) {
47653                     ret = fe;
47654                 }
47655                 fe.form = this;
47656                 if (fe.store) {
47657                     fe.store.form = this;
47658                 }
47659                 if (fe.isLayout) {  
47660                          
47661                     this.start(fe);
47662                     this.allItems.push(fe);
47663                     if (fe.items && fe.addxtype) {
47664                         fe.addxtype.apply(fe, fe.items);
47665                         delete fe.items;
47666                     }
47667                      this.end();
47668                     continue;
47669                 }
47670                 
47671                 
47672                  
47673                 this.add(fe);
47674               //  console.log('adding ' + ar[i].xtype);
47675             }
47676             if (ar[i].xtype == 'Button') {  
47677                 //console.log('adding button');
47678                 //console.log(ar[i]);
47679                 this.addButton(ar[i]);
47680                 this.allItems.push(fe);
47681                 continue;
47682             }
47683             
47684             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
47685                 alert('end is not supported on xtype any more, use items');
47686             //    this.end();
47687             //    //console.log('adding end');
47688             }
47689             
47690         }
47691         return ret;
47692     },
47693     
47694     /**
47695      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
47696      * option "monitorValid"
47697      */
47698     startMonitoring : function(){
47699         if(!this.bound){
47700             this.bound = true;
47701             Roo.TaskMgr.start({
47702                 run : this.bindHandler,
47703                 interval : this.monitorPoll || 200,
47704                 scope: this
47705             });
47706         }
47707     },
47708
47709     /**
47710      * Stops monitoring of the valid state of this form
47711      */
47712     stopMonitoring : function(){
47713         this.bound = false;
47714     },
47715
47716     // private
47717     bindHandler : function(){
47718         if(!this.bound){
47719             return false; // stops binding
47720         }
47721         var valid = true;
47722         this.items.each(function(f){
47723             if(!f.isValid(true)){
47724                 valid = false;
47725                 return false;
47726             }
47727         });
47728         for(var i = 0, len = this.buttons.length; i < len; i++){
47729             var btn = this.buttons[i];
47730             if(btn.formBind === true && btn.disabled === valid){
47731                 btn.setDisabled(!valid);
47732             }
47733         }
47734         this.fireEvent('clientvalidation', this, valid);
47735     }
47736     
47737     
47738     
47739     
47740     
47741     
47742     
47743     
47744 });
47745
47746
47747 // back compat
47748 Roo.Form = Roo.form.Form;
47749 /*
47750  * Based on:
47751  * Ext JS Library 1.1.1
47752  * Copyright(c) 2006-2007, Ext JS, LLC.
47753  *
47754  * Originally Released Under LGPL - original licence link has changed is not relivant.
47755  *
47756  * Fork - LGPL
47757  * <script type="text/javascript">
47758  */
47759
47760 // as we use this in bootstrap.
47761 Roo.namespace('Roo.form');
47762  /**
47763  * @class Roo.form.Action
47764  * Internal Class used to handle form actions
47765  * @constructor
47766  * @param {Roo.form.BasicForm} el The form element or its id
47767  * @param {Object} config Configuration options
47768  */
47769
47770  
47771  
47772 // define the action interface
47773 Roo.form.Action = function(form, options){
47774     this.form = form;
47775     this.options = options || {};
47776 };
47777 /**
47778  * Client Validation Failed
47779  * @const 
47780  */
47781 Roo.form.Action.CLIENT_INVALID = 'client';
47782 /**
47783  * Server Validation Failed
47784  * @const 
47785  */
47786 Roo.form.Action.SERVER_INVALID = 'server';
47787  /**
47788  * Connect to Server Failed
47789  * @const 
47790  */
47791 Roo.form.Action.CONNECT_FAILURE = 'connect';
47792 /**
47793  * Reading Data from Server Failed
47794  * @const 
47795  */
47796 Roo.form.Action.LOAD_FAILURE = 'load';
47797
47798 Roo.form.Action.prototype = {
47799     type : 'default',
47800     failureType : undefined,
47801     response : undefined,
47802     result : undefined,
47803
47804     // interface method
47805     run : function(options){
47806
47807     },
47808
47809     // interface method
47810     success : function(response){
47811
47812     },
47813
47814     // interface method
47815     handleResponse : function(response){
47816
47817     },
47818
47819     // default connection failure
47820     failure : function(response){
47821         
47822         this.response = response;
47823         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47824         this.form.afterAction(this, false);
47825     },
47826
47827     processResponse : function(response){
47828         this.response = response;
47829         if(!response.responseText){
47830             return true;
47831         }
47832         this.result = this.handleResponse(response);
47833         return this.result;
47834     },
47835
47836     // utility functions used internally
47837     getUrl : function(appendParams){
47838         var url = this.options.url || this.form.url || this.form.el.dom.action;
47839         if(appendParams){
47840             var p = this.getParams();
47841             if(p){
47842                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
47843             }
47844         }
47845         return url;
47846     },
47847
47848     getMethod : function(){
47849         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
47850     },
47851
47852     getParams : function(){
47853         var bp = this.form.baseParams;
47854         var p = this.options.params;
47855         if(p){
47856             if(typeof p == "object"){
47857                 p = Roo.urlEncode(Roo.applyIf(p, bp));
47858             }else if(typeof p == 'string' && bp){
47859                 p += '&' + Roo.urlEncode(bp);
47860             }
47861         }else if(bp){
47862             p = Roo.urlEncode(bp);
47863         }
47864         return p;
47865     },
47866
47867     createCallback : function(){
47868         return {
47869             success: this.success,
47870             failure: this.failure,
47871             scope: this,
47872             timeout: (this.form.timeout*1000),
47873             upload: this.form.fileUpload ? this.success : undefined
47874         };
47875     }
47876 };
47877
47878 Roo.form.Action.Submit = function(form, options){
47879     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
47880 };
47881
47882 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
47883     type : 'submit',
47884
47885     haveProgress : false,
47886     uploadComplete : false,
47887     
47888     // uploadProgress indicator.
47889     uploadProgress : function()
47890     {
47891         if (!this.form.progressUrl) {
47892             return;
47893         }
47894         
47895         if (!this.haveProgress) {
47896             Roo.MessageBox.progress("Uploading", "Uploading");
47897         }
47898         if (this.uploadComplete) {
47899            Roo.MessageBox.hide();
47900            return;
47901         }
47902         
47903         this.haveProgress = true;
47904    
47905         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
47906         
47907         var c = new Roo.data.Connection();
47908         c.request({
47909             url : this.form.progressUrl,
47910             params: {
47911                 id : uid
47912             },
47913             method: 'GET',
47914             success : function(req){
47915                //console.log(data);
47916                 var rdata = false;
47917                 var edata;
47918                 try  {
47919                    rdata = Roo.decode(req.responseText)
47920                 } catch (e) {
47921                     Roo.log("Invalid data from server..");
47922                     Roo.log(edata);
47923                     return;
47924                 }
47925                 if (!rdata || !rdata.success) {
47926                     Roo.log(rdata);
47927                     Roo.MessageBox.alert(Roo.encode(rdata));
47928                     return;
47929                 }
47930                 var data = rdata.data;
47931                 
47932                 if (this.uploadComplete) {
47933                    Roo.MessageBox.hide();
47934                    return;
47935                 }
47936                    
47937                 if (data){
47938                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
47939                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
47940                     );
47941                 }
47942                 this.uploadProgress.defer(2000,this);
47943             },
47944        
47945             failure: function(data) {
47946                 Roo.log('progress url failed ');
47947                 Roo.log(data);
47948             },
47949             scope : this
47950         });
47951            
47952     },
47953     
47954     
47955     run : function()
47956     {
47957         // run get Values on the form, so it syncs any secondary forms.
47958         this.form.getValues();
47959         
47960         var o = this.options;
47961         var method = this.getMethod();
47962         var isPost = method == 'POST';
47963         if(o.clientValidation === false || this.form.isValid()){
47964             
47965             if (this.form.progressUrl) {
47966                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
47967                     (new Date() * 1) + '' + Math.random());
47968                     
47969             } 
47970             
47971             
47972             Roo.Ajax.request(Roo.apply(this.createCallback(), {
47973                 form:this.form.el.dom,
47974                 url:this.getUrl(!isPost),
47975                 method: method,
47976                 params:isPost ? this.getParams() : null,
47977                 isUpload: this.form.fileUpload
47978             }));
47979             
47980             this.uploadProgress();
47981
47982         }else if (o.clientValidation !== false){ // client validation failed
47983             this.failureType = Roo.form.Action.CLIENT_INVALID;
47984             this.form.afterAction(this, false);
47985         }
47986     },
47987
47988     success : function(response)
47989     {
47990         this.uploadComplete= true;
47991         if (this.haveProgress) {
47992             Roo.MessageBox.hide();
47993         }
47994         
47995         
47996         var result = this.processResponse(response);
47997         if(result === true || result.success){
47998             this.form.afterAction(this, true);
47999             return;
48000         }
48001         if(result.errors){
48002             this.form.markInvalid(result.errors);
48003             this.failureType = Roo.form.Action.SERVER_INVALID;
48004         }
48005         this.form.afterAction(this, false);
48006     },
48007     failure : function(response)
48008     {
48009         this.uploadComplete= true;
48010         if (this.haveProgress) {
48011             Roo.MessageBox.hide();
48012         }
48013         
48014         this.response = response;
48015         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48016         this.form.afterAction(this, false);
48017     },
48018     
48019     handleResponse : function(response){
48020         if(this.form.errorReader){
48021             var rs = this.form.errorReader.read(response);
48022             var errors = [];
48023             if(rs.records){
48024                 for(var i = 0, len = rs.records.length; i < len; i++) {
48025                     var r = rs.records[i];
48026                     errors[i] = r.data;
48027                 }
48028             }
48029             if(errors.length < 1){
48030                 errors = null;
48031             }
48032             return {
48033                 success : rs.success,
48034                 errors : errors
48035             };
48036         }
48037         var ret = false;
48038         try {
48039             ret = Roo.decode(response.responseText);
48040         } catch (e) {
48041             ret = {
48042                 success: false,
48043                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
48044                 errors : []
48045             };
48046         }
48047         return ret;
48048         
48049     }
48050 });
48051
48052
48053 Roo.form.Action.Load = function(form, options){
48054     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
48055     this.reader = this.form.reader;
48056 };
48057
48058 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
48059     type : 'load',
48060
48061     run : function(){
48062         
48063         Roo.Ajax.request(Roo.apply(
48064                 this.createCallback(), {
48065                     method:this.getMethod(),
48066                     url:this.getUrl(false),
48067                     params:this.getParams()
48068         }));
48069     },
48070
48071     success : function(response){
48072         
48073         var result = this.processResponse(response);
48074         if(result === true || !result.success || !result.data){
48075             this.failureType = Roo.form.Action.LOAD_FAILURE;
48076             this.form.afterAction(this, false);
48077             return;
48078         }
48079         this.form.clearInvalid();
48080         this.form.setValues(result.data);
48081         this.form.afterAction(this, true);
48082     },
48083
48084     handleResponse : function(response){
48085         if(this.form.reader){
48086             var rs = this.form.reader.read(response);
48087             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
48088             return {
48089                 success : rs.success,
48090                 data : data
48091             };
48092         }
48093         return Roo.decode(response.responseText);
48094     }
48095 });
48096
48097 Roo.form.Action.ACTION_TYPES = {
48098     'load' : Roo.form.Action.Load,
48099     'submit' : Roo.form.Action.Submit
48100 };/*
48101  * Based on:
48102  * Ext JS Library 1.1.1
48103  * Copyright(c) 2006-2007, Ext JS, LLC.
48104  *
48105  * Originally Released Under LGPL - original licence link has changed is not relivant.
48106  *
48107  * Fork - LGPL
48108  * <script type="text/javascript">
48109  */
48110  
48111 /**
48112  * @class Roo.form.Layout
48113  * @extends Roo.Component
48114  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
48115  * @constructor
48116  * @param {Object} config Configuration options
48117  */
48118 Roo.form.Layout = function(config){
48119     var xitems = [];
48120     if (config.items) {
48121         xitems = config.items;
48122         delete config.items;
48123     }
48124     Roo.form.Layout.superclass.constructor.call(this, config);
48125     this.stack = [];
48126     Roo.each(xitems, this.addxtype, this);
48127      
48128 };
48129
48130 Roo.extend(Roo.form.Layout, Roo.Component, {
48131     /**
48132      * @cfg {String/Object} autoCreate
48133      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
48134      */
48135     /**
48136      * @cfg {String/Object/Function} style
48137      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
48138      * a function which returns such a specification.
48139      */
48140     /**
48141      * @cfg {String} labelAlign
48142      * Valid values are "left," "top" and "right" (defaults to "left")
48143      */
48144     /**
48145      * @cfg {Number} labelWidth
48146      * Fixed width in pixels of all field labels (defaults to undefined)
48147      */
48148     /**
48149      * @cfg {Boolean} clear
48150      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
48151      */
48152     clear : true,
48153     /**
48154      * @cfg {String} labelSeparator
48155      * The separator to use after field labels (defaults to ':')
48156      */
48157     labelSeparator : ':',
48158     /**
48159      * @cfg {Boolean} hideLabels
48160      * True to suppress the display of field labels in this layout (defaults to false)
48161      */
48162     hideLabels : false,
48163
48164     // private
48165     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
48166     
48167     isLayout : true,
48168     
48169     // private
48170     onRender : function(ct, position){
48171         if(this.el){ // from markup
48172             this.el = Roo.get(this.el);
48173         }else {  // generate
48174             var cfg = this.getAutoCreate();
48175             this.el = ct.createChild(cfg, position);
48176         }
48177         if(this.style){
48178             this.el.applyStyles(this.style);
48179         }
48180         if(this.labelAlign){
48181             this.el.addClass('x-form-label-'+this.labelAlign);
48182         }
48183         if(this.hideLabels){
48184             this.labelStyle = "display:none";
48185             this.elementStyle = "padding-left:0;";
48186         }else{
48187             if(typeof this.labelWidth == 'number'){
48188                 this.labelStyle = "width:"+this.labelWidth+"px;";
48189                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
48190             }
48191             if(this.labelAlign == 'top'){
48192                 this.labelStyle = "width:auto;";
48193                 this.elementStyle = "padding-left:0;";
48194             }
48195         }
48196         var stack = this.stack;
48197         var slen = stack.length;
48198         if(slen > 0){
48199             if(!this.fieldTpl){
48200                 var t = new Roo.Template(
48201                     '<div class="x-form-item {5}">',
48202                         '<label for="{0}" style="{2}">{1}{4}</label>',
48203                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48204                         '</div>',
48205                     '</div><div class="x-form-clear-left"></div>'
48206                 );
48207                 t.disableFormats = true;
48208                 t.compile();
48209                 Roo.form.Layout.prototype.fieldTpl = t;
48210             }
48211             for(var i = 0; i < slen; i++) {
48212                 if(stack[i].isFormField){
48213                     this.renderField(stack[i]);
48214                 }else{
48215                     this.renderComponent(stack[i]);
48216                 }
48217             }
48218         }
48219         if(this.clear){
48220             this.el.createChild({cls:'x-form-clear'});
48221         }
48222     },
48223
48224     // private
48225     renderField : function(f){
48226         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
48227                f.id, //0
48228                f.fieldLabel, //1
48229                f.labelStyle||this.labelStyle||'', //2
48230                this.elementStyle||'', //3
48231                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
48232                f.itemCls||this.itemCls||''  //5
48233        ], true).getPrevSibling());
48234     },
48235
48236     // private
48237     renderComponent : function(c){
48238         c.render(c.isLayout ? this.el : this.el.createChild());    
48239     },
48240     /**
48241      * Adds a object form elements (using the xtype property as the factory method.)
48242      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
48243      * @param {Object} config 
48244      */
48245     addxtype : function(o)
48246     {
48247         // create the lement.
48248         o.form = this.form;
48249         var fe = Roo.factory(o, Roo.form);
48250         this.form.allItems.push(fe);
48251         this.stack.push(fe);
48252         
48253         if (fe.isFormField) {
48254             this.form.items.add(fe);
48255         }
48256          
48257         return fe;
48258     }
48259 });
48260
48261 /**
48262  * @class Roo.form.Column
48263  * @extends Roo.form.Layout
48264  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
48265  * @constructor
48266  * @param {Object} config Configuration options
48267  */
48268 Roo.form.Column = function(config){
48269     Roo.form.Column.superclass.constructor.call(this, config);
48270 };
48271
48272 Roo.extend(Roo.form.Column, Roo.form.Layout, {
48273     /**
48274      * @cfg {Number/String} width
48275      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48276      */
48277     /**
48278      * @cfg {String/Object} autoCreate
48279      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
48280      */
48281
48282     // private
48283     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
48284
48285     // private
48286     onRender : function(ct, position){
48287         Roo.form.Column.superclass.onRender.call(this, ct, position);
48288         if(this.width){
48289             this.el.setWidth(this.width);
48290         }
48291     }
48292 });
48293
48294
48295 /**
48296  * @class Roo.form.Row
48297  * @extends Roo.form.Layout
48298  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
48299  * @constructor
48300  * @param {Object} config Configuration options
48301  */
48302
48303  
48304 Roo.form.Row = function(config){
48305     Roo.form.Row.superclass.constructor.call(this, config);
48306 };
48307  
48308 Roo.extend(Roo.form.Row, Roo.form.Layout, {
48309       /**
48310      * @cfg {Number/String} width
48311      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48312      */
48313     /**
48314      * @cfg {Number/String} height
48315      * The fixed height of the column in pixels or CSS value (defaults to "auto")
48316      */
48317     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
48318     
48319     padWidth : 20,
48320     // private
48321     onRender : function(ct, position){
48322         //console.log('row render');
48323         if(!this.rowTpl){
48324             var t = new Roo.Template(
48325                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
48326                     '<label for="{0}" style="{2}">{1}{4}</label>',
48327                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48328                     '</div>',
48329                 '</div>'
48330             );
48331             t.disableFormats = true;
48332             t.compile();
48333             Roo.form.Layout.prototype.rowTpl = t;
48334         }
48335         this.fieldTpl = this.rowTpl;
48336         
48337         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
48338         var labelWidth = 100;
48339         
48340         if ((this.labelAlign != 'top')) {
48341             if (typeof this.labelWidth == 'number') {
48342                 labelWidth = this.labelWidth
48343             }
48344             this.padWidth =  20 + labelWidth;
48345             
48346         }
48347         
48348         Roo.form.Column.superclass.onRender.call(this, ct, position);
48349         if(this.width){
48350             this.el.setWidth(this.width);
48351         }
48352         if(this.height){
48353             this.el.setHeight(this.height);
48354         }
48355     },
48356     
48357     // private
48358     renderField : function(f){
48359         f.fieldEl = this.fieldTpl.append(this.el, [
48360                f.id, f.fieldLabel,
48361                f.labelStyle||this.labelStyle||'',
48362                this.elementStyle||'',
48363                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
48364                f.itemCls||this.itemCls||'',
48365                f.width ? f.width + this.padWidth : 160 + this.padWidth
48366        ],true);
48367     }
48368 });
48369  
48370
48371 /**
48372  * @class Roo.form.FieldSet
48373  * @extends Roo.form.Layout
48374  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
48375  * @constructor
48376  * @param {Object} config Configuration options
48377  */
48378 Roo.form.FieldSet = function(config){
48379     Roo.form.FieldSet.superclass.constructor.call(this, config);
48380 };
48381
48382 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
48383     /**
48384      * @cfg {String} legend
48385      * The text to display as the legend for the FieldSet (defaults to '')
48386      */
48387     /**
48388      * @cfg {String/Object} autoCreate
48389      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
48390      */
48391
48392     // private
48393     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
48394
48395     // private
48396     onRender : function(ct, position){
48397         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
48398         if(this.legend){
48399             this.setLegend(this.legend);
48400         }
48401     },
48402
48403     // private
48404     setLegend : function(text){
48405         if(this.rendered){
48406             this.el.child('legend').update(text);
48407         }
48408     }
48409 });/*
48410  * Based on:
48411  * Ext JS Library 1.1.1
48412  * Copyright(c) 2006-2007, Ext JS, LLC.
48413  *
48414  * Originally Released Under LGPL - original licence link has changed is not relivant.
48415  *
48416  * Fork - LGPL
48417  * <script type="text/javascript">
48418  */
48419 /**
48420  * @class Roo.form.VTypes
48421  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
48422  * @singleton
48423  */
48424 Roo.form.VTypes = function(){
48425     // closure these in so they are only created once.
48426     var alpha = /^[a-zA-Z_]+$/;
48427     var alphanum = /^[a-zA-Z0-9_]+$/;
48428     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
48429     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
48430
48431     // All these messages and functions are configurable
48432     return {
48433         /**
48434          * The function used to validate email addresses
48435          * @param {String} value The email address
48436          */
48437         'email' : function(v){
48438             return email.test(v);
48439         },
48440         /**
48441          * The error text to display when the email validation function returns false
48442          * @type String
48443          */
48444         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
48445         /**
48446          * The keystroke filter mask to be applied on email input
48447          * @type RegExp
48448          */
48449         'emailMask' : /[a-z0-9_\.\-@]/i,
48450
48451         /**
48452          * The function used to validate URLs
48453          * @param {String} value The URL
48454          */
48455         'url' : function(v){
48456             return url.test(v);
48457         },
48458         /**
48459          * The error text to display when the url validation function returns false
48460          * @type String
48461          */
48462         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
48463         
48464         /**
48465          * The function used to validate alpha values
48466          * @param {String} value The value
48467          */
48468         'alpha' : function(v){
48469             return alpha.test(v);
48470         },
48471         /**
48472          * The error text to display when the alpha validation function returns false
48473          * @type String
48474          */
48475         'alphaText' : 'This field should only contain letters and _',
48476         /**
48477          * The keystroke filter mask to be applied on alpha input
48478          * @type RegExp
48479          */
48480         'alphaMask' : /[a-z_]/i,
48481
48482         /**
48483          * The function used to validate alphanumeric values
48484          * @param {String} value The value
48485          */
48486         'alphanum' : function(v){
48487             return alphanum.test(v);
48488         },
48489         /**
48490          * The error text to display when the alphanumeric validation function returns false
48491          * @type String
48492          */
48493         'alphanumText' : 'This field should only contain letters, numbers and _',
48494         /**
48495          * The keystroke filter mask to be applied on alphanumeric input
48496          * @type RegExp
48497          */
48498         'alphanumMask' : /[a-z0-9_]/i
48499     };
48500 }();//<script type="text/javascript">
48501
48502 /**
48503  * @class Roo.form.FCKeditor
48504  * @extends Roo.form.TextArea
48505  * Wrapper around the FCKEditor http://www.fckeditor.net
48506  * @constructor
48507  * Creates a new FCKeditor
48508  * @param {Object} config Configuration options
48509  */
48510 Roo.form.FCKeditor = function(config){
48511     Roo.form.FCKeditor.superclass.constructor.call(this, config);
48512     this.addEvents({
48513          /**
48514          * @event editorinit
48515          * Fired when the editor is initialized - you can add extra handlers here..
48516          * @param {FCKeditor} this
48517          * @param {Object} the FCK object.
48518          */
48519         editorinit : true
48520     });
48521     
48522     
48523 };
48524 Roo.form.FCKeditor.editors = { };
48525 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
48526 {
48527     //defaultAutoCreate : {
48528     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
48529     //},
48530     // private
48531     /**
48532      * @cfg {Object} fck options - see fck manual for details.
48533      */
48534     fckconfig : false,
48535     
48536     /**
48537      * @cfg {Object} fck toolbar set (Basic or Default)
48538      */
48539     toolbarSet : 'Basic',
48540     /**
48541      * @cfg {Object} fck BasePath
48542      */ 
48543     basePath : '/fckeditor/',
48544     
48545     
48546     frame : false,
48547     
48548     value : '',
48549     
48550    
48551     onRender : function(ct, position)
48552     {
48553         if(!this.el){
48554             this.defaultAutoCreate = {
48555                 tag: "textarea",
48556                 style:"width:300px;height:60px;",
48557                 autocomplete: "new-password"
48558             };
48559         }
48560         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
48561         /*
48562         if(this.grow){
48563             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
48564             if(this.preventScrollbars){
48565                 this.el.setStyle("overflow", "hidden");
48566             }
48567             this.el.setHeight(this.growMin);
48568         }
48569         */
48570         //console.log('onrender' + this.getId() );
48571         Roo.form.FCKeditor.editors[this.getId()] = this;
48572          
48573
48574         this.replaceTextarea() ;
48575         
48576     },
48577     
48578     getEditor : function() {
48579         return this.fckEditor;
48580     },
48581     /**
48582      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
48583      * @param {Mixed} value The value to set
48584      */
48585     
48586     
48587     setValue : function(value)
48588     {
48589         //console.log('setValue: ' + value);
48590         
48591         if(typeof(value) == 'undefined') { // not sure why this is happending...
48592             return;
48593         }
48594         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48595         
48596         //if(!this.el || !this.getEditor()) {
48597         //    this.value = value;
48598             //this.setValue.defer(100,this,[value]);    
48599         //    return;
48600         //} 
48601         
48602         if(!this.getEditor()) {
48603             return;
48604         }
48605         
48606         this.getEditor().SetData(value);
48607         
48608         //
48609
48610     },
48611
48612     /**
48613      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
48614      * @return {Mixed} value The field value
48615      */
48616     getValue : function()
48617     {
48618         
48619         if (this.frame && this.frame.dom.style.display == 'none') {
48620             return Roo.form.FCKeditor.superclass.getValue.call(this);
48621         }
48622         
48623         if(!this.el || !this.getEditor()) {
48624            
48625            // this.getValue.defer(100,this); 
48626             return this.value;
48627         }
48628        
48629         
48630         var value=this.getEditor().GetData();
48631         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48632         return Roo.form.FCKeditor.superclass.getValue.call(this);
48633         
48634
48635     },
48636
48637     /**
48638      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
48639      * @return {Mixed} value The field value
48640      */
48641     getRawValue : function()
48642     {
48643         if (this.frame && this.frame.dom.style.display == 'none') {
48644             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48645         }
48646         
48647         if(!this.el || !this.getEditor()) {
48648             //this.getRawValue.defer(100,this); 
48649             return this.value;
48650             return;
48651         }
48652         
48653         
48654         
48655         var value=this.getEditor().GetData();
48656         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
48657         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48658          
48659     },
48660     
48661     setSize : function(w,h) {
48662         
48663         
48664         
48665         //if (this.frame && this.frame.dom.style.display == 'none') {
48666         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48667         //    return;
48668         //}
48669         //if(!this.el || !this.getEditor()) {
48670         //    this.setSize.defer(100,this, [w,h]); 
48671         //    return;
48672         //}
48673         
48674         
48675         
48676         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48677         
48678         this.frame.dom.setAttribute('width', w);
48679         this.frame.dom.setAttribute('height', h);
48680         this.frame.setSize(w,h);
48681         
48682     },
48683     
48684     toggleSourceEdit : function(value) {
48685         
48686       
48687          
48688         this.el.dom.style.display = value ? '' : 'none';
48689         this.frame.dom.style.display = value ?  'none' : '';
48690         
48691     },
48692     
48693     
48694     focus: function(tag)
48695     {
48696         if (this.frame.dom.style.display == 'none') {
48697             return Roo.form.FCKeditor.superclass.focus.call(this);
48698         }
48699         if(!this.el || !this.getEditor()) {
48700             this.focus.defer(100,this, [tag]); 
48701             return;
48702         }
48703         
48704         
48705         
48706         
48707         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
48708         this.getEditor().Focus();
48709         if (tgs.length) {
48710             if (!this.getEditor().Selection.GetSelection()) {
48711                 this.focus.defer(100,this, [tag]); 
48712                 return;
48713             }
48714             
48715             
48716             var r = this.getEditor().EditorDocument.createRange();
48717             r.setStart(tgs[0],0);
48718             r.setEnd(tgs[0],0);
48719             this.getEditor().Selection.GetSelection().removeAllRanges();
48720             this.getEditor().Selection.GetSelection().addRange(r);
48721             this.getEditor().Focus();
48722         }
48723         
48724     },
48725     
48726     
48727     
48728     replaceTextarea : function()
48729     {
48730         if ( document.getElementById( this.getId() + '___Frame' ) ) {
48731             return ;
48732         }
48733         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
48734         //{
48735             // We must check the elements firstly using the Id and then the name.
48736         var oTextarea = document.getElementById( this.getId() );
48737         
48738         var colElementsByName = document.getElementsByName( this.getId() ) ;
48739          
48740         oTextarea.style.display = 'none' ;
48741
48742         if ( oTextarea.tabIndex ) {            
48743             this.TabIndex = oTextarea.tabIndex ;
48744         }
48745         
48746         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
48747         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
48748         this.frame = Roo.get(this.getId() + '___Frame')
48749     },
48750     
48751     _getConfigHtml : function()
48752     {
48753         var sConfig = '' ;
48754
48755         for ( var o in this.fckconfig ) {
48756             sConfig += sConfig.length > 0  ? '&amp;' : '';
48757             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
48758         }
48759
48760         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
48761     },
48762     
48763     
48764     _getIFrameHtml : function()
48765     {
48766         var sFile = 'fckeditor.html' ;
48767         /* no idea what this is about..
48768         try
48769         {
48770             if ( (/fcksource=true/i).test( window.top.location.search ) )
48771                 sFile = 'fckeditor.original.html' ;
48772         }
48773         catch (e) { 
48774         */
48775
48776         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
48777         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
48778         
48779         
48780         var html = '<iframe id="' + this.getId() +
48781             '___Frame" src="' + sLink +
48782             '" width="' + this.width +
48783             '" height="' + this.height + '"' +
48784             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
48785             ' frameborder="0" scrolling="no"></iframe>' ;
48786
48787         return html ;
48788     },
48789     
48790     _insertHtmlBefore : function( html, element )
48791     {
48792         if ( element.insertAdjacentHTML )       {
48793             // IE
48794             element.insertAdjacentHTML( 'beforeBegin', html ) ;
48795         } else { // Gecko
48796             var oRange = document.createRange() ;
48797             oRange.setStartBefore( element ) ;
48798             var oFragment = oRange.createContextualFragment( html );
48799             element.parentNode.insertBefore( oFragment, element ) ;
48800         }
48801     }
48802     
48803     
48804   
48805     
48806     
48807     
48808     
48809
48810 });
48811
48812 //Roo.reg('fckeditor', Roo.form.FCKeditor);
48813
48814 function FCKeditor_OnComplete(editorInstance){
48815     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
48816     f.fckEditor = editorInstance;
48817     //console.log("loaded");
48818     f.fireEvent('editorinit', f, editorInstance);
48819
48820   
48821
48822  
48823
48824
48825
48826
48827
48828
48829
48830
48831
48832
48833
48834
48835
48836
48837
48838 //<script type="text/javascript">
48839 /**
48840  * @class Roo.form.GridField
48841  * @extends Roo.form.Field
48842  * Embed a grid (or editable grid into a form)
48843  * STATUS ALPHA
48844  * 
48845  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
48846  * it needs 
48847  * xgrid.store = Roo.data.Store
48848  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
48849  * xgrid.store.reader = Roo.data.JsonReader 
48850  * 
48851  * 
48852  * @constructor
48853  * Creates a new GridField
48854  * @param {Object} config Configuration options
48855  */
48856 Roo.form.GridField = function(config){
48857     Roo.form.GridField.superclass.constructor.call(this, config);
48858      
48859 };
48860
48861 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
48862     /**
48863      * @cfg {Number} width  - used to restrict width of grid..
48864      */
48865     width : 100,
48866     /**
48867      * @cfg {Number} height - used to restrict height of grid..
48868      */
48869     height : 50,
48870      /**
48871      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
48872          * 
48873          *}
48874      */
48875     xgrid : false, 
48876     /**
48877      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48878      * {tag: "input", type: "checkbox", autocomplete: "off"})
48879      */
48880    // defaultAutoCreate : { tag: 'div' },
48881     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
48882     /**
48883      * @cfg {String} addTitle Text to include for adding a title.
48884      */
48885     addTitle : false,
48886     //
48887     onResize : function(){
48888         Roo.form.Field.superclass.onResize.apply(this, arguments);
48889     },
48890
48891     initEvents : function(){
48892         // Roo.form.Checkbox.superclass.initEvents.call(this);
48893         // has no events...
48894        
48895     },
48896
48897
48898     getResizeEl : function(){
48899         return this.wrap;
48900     },
48901
48902     getPositionEl : function(){
48903         return this.wrap;
48904     },
48905
48906     // private
48907     onRender : function(ct, position){
48908         
48909         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
48910         var style = this.style;
48911         delete this.style;
48912         
48913         Roo.form.GridField.superclass.onRender.call(this, ct, position);
48914         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
48915         this.viewEl = this.wrap.createChild({ tag: 'div' });
48916         if (style) {
48917             this.viewEl.applyStyles(style);
48918         }
48919         if (this.width) {
48920             this.viewEl.setWidth(this.width);
48921         }
48922         if (this.height) {
48923             this.viewEl.setHeight(this.height);
48924         }
48925         //if(this.inputValue !== undefined){
48926         //this.setValue(this.value);
48927         
48928         
48929         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
48930         
48931         
48932         this.grid.render();
48933         this.grid.getDataSource().on('remove', this.refreshValue, this);
48934         this.grid.getDataSource().on('update', this.refreshValue, this);
48935         this.grid.on('afteredit', this.refreshValue, this);
48936  
48937     },
48938      
48939     
48940     /**
48941      * Sets the value of the item. 
48942      * @param {String} either an object  or a string..
48943      */
48944     setValue : function(v){
48945         //this.value = v;
48946         v = v || []; // empty set..
48947         // this does not seem smart - it really only affects memoryproxy grids..
48948         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
48949             var ds = this.grid.getDataSource();
48950             // assumes a json reader..
48951             var data = {}
48952             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
48953             ds.loadData( data);
48954         }
48955         // clear selection so it does not get stale.
48956         if (this.grid.sm) { 
48957             this.grid.sm.clearSelections();
48958         }
48959         
48960         Roo.form.GridField.superclass.setValue.call(this, v);
48961         this.refreshValue();
48962         // should load data in the grid really....
48963     },
48964     
48965     // private
48966     refreshValue: function() {
48967          var val = [];
48968         this.grid.getDataSource().each(function(r) {
48969             val.push(r.data);
48970         });
48971         this.el.dom.value = Roo.encode(val);
48972     }
48973     
48974      
48975     
48976     
48977 });/*
48978  * Based on:
48979  * Ext JS Library 1.1.1
48980  * Copyright(c) 2006-2007, Ext JS, LLC.
48981  *
48982  * Originally Released Under LGPL - original licence link has changed is not relivant.
48983  *
48984  * Fork - LGPL
48985  * <script type="text/javascript">
48986  */
48987 /**
48988  * @class Roo.form.DisplayField
48989  * @extends Roo.form.Field
48990  * A generic Field to display non-editable data.
48991  * @cfg {Boolean} closable (true|false) default false
48992  * @constructor
48993  * Creates a new Display Field item.
48994  * @param {Object} config Configuration options
48995  */
48996 Roo.form.DisplayField = function(config){
48997     Roo.form.DisplayField.superclass.constructor.call(this, config);
48998     
48999     this.addEvents({
49000         /**
49001          * @event close
49002          * Fires after the click the close btn
49003              * @param {Roo.form.DisplayField} this
49004              */
49005         close : true
49006     });
49007 };
49008
49009 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
49010     inputType:      'hidden',
49011     allowBlank:     true,
49012     readOnly:         true,
49013     
49014  
49015     /**
49016      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49017      */
49018     focusClass : undefined,
49019     /**
49020      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49021      */
49022     fieldClass: 'x-form-field',
49023     
49024      /**
49025      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
49026      */
49027     valueRenderer: undefined,
49028     
49029     width: 100,
49030     /**
49031      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49032      * {tag: "input", type: "checkbox", autocomplete: "off"})
49033      */
49034      
49035  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
49036  
49037     closable : false,
49038     
49039     onResize : function(){
49040         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
49041         
49042     },
49043
49044     initEvents : function(){
49045         // Roo.form.Checkbox.superclass.initEvents.call(this);
49046         // has no events...
49047         
49048         if(this.closable){
49049             this.closeEl.on('click', this.onClose, this);
49050         }
49051        
49052     },
49053
49054
49055     getResizeEl : function(){
49056         return this.wrap;
49057     },
49058
49059     getPositionEl : function(){
49060         return this.wrap;
49061     },
49062
49063     // private
49064     onRender : function(ct, position){
49065         
49066         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
49067         //if(this.inputValue !== undefined){
49068         this.wrap = this.el.wrap();
49069         
49070         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
49071         
49072         if(this.closable){
49073             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
49074         }
49075         
49076         if (this.bodyStyle) {
49077             this.viewEl.applyStyles(this.bodyStyle);
49078         }
49079         //this.viewEl.setStyle('padding', '2px');
49080         
49081         this.setValue(this.value);
49082         
49083     },
49084 /*
49085     // private
49086     initValue : Roo.emptyFn,
49087
49088   */
49089
49090         // private
49091     onClick : function(){
49092         
49093     },
49094
49095     /**
49096      * Sets the checked state of the checkbox.
49097      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
49098      */
49099     setValue : function(v){
49100         this.value = v;
49101         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
49102         // this might be called before we have a dom element..
49103         if (!this.viewEl) {
49104             return;
49105         }
49106         this.viewEl.dom.innerHTML = html;
49107         Roo.form.DisplayField.superclass.setValue.call(this, v);
49108
49109     },
49110     
49111     onClose : function(e)
49112     {
49113         e.preventDefault();
49114         
49115         this.fireEvent('close', this);
49116     }
49117 });/*
49118  * 
49119  * Licence- LGPL
49120  * 
49121  */
49122
49123 /**
49124  * @class Roo.form.DayPicker
49125  * @extends Roo.form.Field
49126  * A Day picker show [M] [T] [W] ....
49127  * @constructor
49128  * Creates a new Day Picker
49129  * @param {Object} config Configuration options
49130  */
49131 Roo.form.DayPicker= function(config){
49132     Roo.form.DayPicker.superclass.constructor.call(this, config);
49133      
49134 };
49135
49136 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
49137     /**
49138      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49139      */
49140     focusClass : undefined,
49141     /**
49142      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49143      */
49144     fieldClass: "x-form-field",
49145    
49146     /**
49147      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49148      * {tag: "input", type: "checkbox", autocomplete: "off"})
49149      */
49150     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
49151     
49152    
49153     actionMode : 'viewEl', 
49154     //
49155     // private
49156  
49157     inputType : 'hidden',
49158     
49159      
49160     inputElement: false, // real input element?
49161     basedOn: false, // ????
49162     
49163     isFormField: true, // not sure where this is needed!!!!
49164
49165     onResize : function(){
49166         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
49167         if(!this.boxLabel){
49168             this.el.alignTo(this.wrap, 'c-c');
49169         }
49170     },
49171
49172     initEvents : function(){
49173         Roo.form.Checkbox.superclass.initEvents.call(this);
49174         this.el.on("click", this.onClick,  this);
49175         this.el.on("change", this.onClick,  this);
49176     },
49177
49178
49179     getResizeEl : function(){
49180         return this.wrap;
49181     },
49182
49183     getPositionEl : function(){
49184         return this.wrap;
49185     },
49186
49187     
49188     // private
49189     onRender : function(ct, position){
49190         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
49191        
49192         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
49193         
49194         var r1 = '<table><tr>';
49195         var r2 = '<tr class="x-form-daypick-icons">';
49196         for (var i=0; i < 7; i++) {
49197             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
49198             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
49199         }
49200         
49201         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
49202         viewEl.select('img').on('click', this.onClick, this);
49203         this.viewEl = viewEl;   
49204         
49205         
49206         // this will not work on Chrome!!!
49207         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
49208         this.el.on('propertychange', this.setFromHidden,  this);  //ie
49209         
49210         
49211           
49212
49213     },
49214
49215     // private
49216     initValue : Roo.emptyFn,
49217
49218     /**
49219      * Returns the checked state of the checkbox.
49220      * @return {Boolean} True if checked, else false
49221      */
49222     getValue : function(){
49223         return this.el.dom.value;
49224         
49225     },
49226
49227         // private
49228     onClick : function(e){ 
49229         //this.setChecked(!this.checked);
49230         Roo.get(e.target).toggleClass('x-menu-item-checked');
49231         this.refreshValue();
49232         //if(this.el.dom.checked != this.checked){
49233         //    this.setValue(this.el.dom.checked);
49234        // }
49235     },
49236     
49237     // private
49238     refreshValue : function()
49239     {
49240         var val = '';
49241         this.viewEl.select('img',true).each(function(e,i,n)  {
49242             val += e.is(".x-menu-item-checked") ? String(n) : '';
49243         });
49244         this.setValue(val, true);
49245     },
49246
49247     /**
49248      * Sets the checked state of the checkbox.
49249      * On is always based on a string comparison between inputValue and the param.
49250      * @param {Boolean/String} value - the value to set 
49251      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
49252      */
49253     setValue : function(v,suppressEvent){
49254         if (!this.el.dom) {
49255             return;
49256         }
49257         var old = this.el.dom.value ;
49258         this.el.dom.value = v;
49259         if (suppressEvent) {
49260             return ;
49261         }
49262          
49263         // update display..
49264         this.viewEl.select('img',true).each(function(e,i,n)  {
49265             
49266             var on = e.is(".x-menu-item-checked");
49267             var newv = v.indexOf(String(n)) > -1;
49268             if (on != newv) {
49269                 e.toggleClass('x-menu-item-checked');
49270             }
49271             
49272         });
49273         
49274         
49275         this.fireEvent('change', this, v, old);
49276         
49277         
49278     },
49279    
49280     // handle setting of hidden value by some other method!!?!?
49281     setFromHidden: function()
49282     {
49283         if(!this.el){
49284             return;
49285         }
49286         //console.log("SET FROM HIDDEN");
49287         //alert('setFrom hidden');
49288         this.setValue(this.el.dom.value);
49289     },
49290     
49291     onDestroy : function()
49292     {
49293         if(this.viewEl){
49294             Roo.get(this.viewEl).remove();
49295         }
49296          
49297         Roo.form.DayPicker.superclass.onDestroy.call(this);
49298     }
49299
49300 });/*
49301  * RooJS Library 1.1.1
49302  * Copyright(c) 2008-2011  Alan Knowles
49303  *
49304  * License - LGPL
49305  */
49306  
49307
49308 /**
49309  * @class Roo.form.ComboCheck
49310  * @extends Roo.form.ComboBox
49311  * A combobox for multiple select items.
49312  *
49313  * FIXME - could do with a reset button..
49314  * 
49315  * @constructor
49316  * Create a new ComboCheck
49317  * @param {Object} config Configuration options
49318  */
49319 Roo.form.ComboCheck = function(config){
49320     Roo.form.ComboCheck.superclass.constructor.call(this, config);
49321     // should verify some data...
49322     // like
49323     // hiddenName = required..
49324     // displayField = required
49325     // valudField == required
49326     var req= [ 'hiddenName', 'displayField', 'valueField' ];
49327     var _t = this;
49328     Roo.each(req, function(e) {
49329         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
49330             throw "Roo.form.ComboCheck : missing value for: " + e;
49331         }
49332     });
49333     
49334     
49335 };
49336
49337 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
49338      
49339      
49340     editable : false,
49341      
49342     selectedClass: 'x-menu-item-checked', 
49343     
49344     // private
49345     onRender : function(ct, position){
49346         var _t = this;
49347         
49348         
49349         
49350         if(!this.tpl){
49351             var cls = 'x-combo-list';
49352
49353             
49354             this.tpl =  new Roo.Template({
49355                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
49356                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
49357                    '<span>{' + this.displayField + '}</span>' +
49358                     '</div>' 
49359                 
49360             });
49361         }
49362  
49363         
49364         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
49365         this.view.singleSelect = false;
49366         this.view.multiSelect = true;
49367         this.view.toggleSelect = true;
49368         this.pageTb.add(new Roo.Toolbar.Fill(), {
49369             
49370             text: 'Done',
49371             handler: function()
49372             {
49373                 _t.collapse();
49374             }
49375         });
49376     },
49377     
49378     onViewOver : function(e, t){
49379         // do nothing...
49380         return;
49381         
49382     },
49383     
49384     onViewClick : function(doFocus,index){
49385         return;
49386         
49387     },
49388     select: function () {
49389         //Roo.log("SELECT CALLED");
49390     },
49391      
49392     selectByValue : function(xv, scrollIntoView){
49393         var ar = this.getValueArray();
49394         var sels = [];
49395         
49396         Roo.each(ar, function(v) {
49397             if(v === undefined || v === null){
49398                 return;
49399             }
49400             var r = this.findRecord(this.valueField, v);
49401             if(r){
49402                 sels.push(this.store.indexOf(r))
49403                 
49404             }
49405         },this);
49406         this.view.select(sels);
49407         return false;
49408     },
49409     
49410     
49411     
49412     onSelect : function(record, index){
49413        // Roo.log("onselect Called");
49414        // this is only called by the clear button now..
49415         this.view.clearSelections();
49416         this.setValue('[]');
49417         if (this.value != this.valueBefore) {
49418             this.fireEvent('change', this, this.value, this.valueBefore);
49419             this.valueBefore = this.value;
49420         }
49421     },
49422     getValueArray : function()
49423     {
49424         var ar = [] ;
49425         
49426         try {
49427             //Roo.log(this.value);
49428             if (typeof(this.value) == 'undefined') {
49429                 return [];
49430             }
49431             var ar = Roo.decode(this.value);
49432             return  ar instanceof Array ? ar : []; //?? valid?
49433             
49434         } catch(e) {
49435             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
49436             return [];
49437         }
49438          
49439     },
49440     expand : function ()
49441     {
49442         
49443         Roo.form.ComboCheck.superclass.expand.call(this);
49444         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
49445         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
49446         
49447
49448     },
49449     
49450     collapse : function(){
49451         Roo.form.ComboCheck.superclass.collapse.call(this);
49452         var sl = this.view.getSelectedIndexes();
49453         var st = this.store;
49454         var nv = [];
49455         var tv = [];
49456         var r;
49457         Roo.each(sl, function(i) {
49458             r = st.getAt(i);
49459             nv.push(r.get(this.valueField));
49460         },this);
49461         this.setValue(Roo.encode(nv));
49462         if (this.value != this.valueBefore) {
49463
49464             this.fireEvent('change', this, this.value, this.valueBefore);
49465             this.valueBefore = this.value;
49466         }
49467         
49468     },
49469     
49470     setValue : function(v){
49471         // Roo.log(v);
49472         this.value = v;
49473         
49474         var vals = this.getValueArray();
49475         var tv = [];
49476         Roo.each(vals, function(k) {
49477             var r = this.findRecord(this.valueField, k);
49478             if(r){
49479                 tv.push(r.data[this.displayField]);
49480             }else if(this.valueNotFoundText !== undefined){
49481                 tv.push( this.valueNotFoundText );
49482             }
49483         },this);
49484        // Roo.log(tv);
49485         
49486         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
49487         this.hiddenField.value = v;
49488         this.value = v;
49489     }
49490     
49491 });/*
49492  * Based on:
49493  * Ext JS Library 1.1.1
49494  * Copyright(c) 2006-2007, Ext JS, LLC.
49495  *
49496  * Originally Released Under LGPL - original licence link has changed is not relivant.
49497  *
49498  * Fork - LGPL
49499  * <script type="text/javascript">
49500  */
49501  
49502 /**
49503  * @class Roo.form.Signature
49504  * @extends Roo.form.Field
49505  * Signature field.  
49506  * @constructor
49507  * 
49508  * @param {Object} config Configuration options
49509  */
49510
49511 Roo.form.Signature = function(config){
49512     Roo.form.Signature.superclass.constructor.call(this, config);
49513     
49514     this.addEvents({// not in used??
49515          /**
49516          * @event confirm
49517          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
49518              * @param {Roo.form.Signature} combo This combo box
49519              */
49520         'confirm' : true,
49521         /**
49522          * @event reset
49523          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
49524              * @param {Roo.form.ComboBox} combo This combo box
49525              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
49526              */
49527         'reset' : true
49528     });
49529 };
49530
49531 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
49532     /**
49533      * @cfg {Object} labels Label to use when rendering a form.
49534      * defaults to 
49535      * labels : { 
49536      *      clear : "Clear",
49537      *      confirm : "Confirm"
49538      *  }
49539      */
49540     labels : { 
49541         clear : "Clear",
49542         confirm : "Confirm"
49543     },
49544     /**
49545      * @cfg {Number} width The signature panel width (defaults to 300)
49546      */
49547     width: 300,
49548     /**
49549      * @cfg {Number} height The signature panel height (defaults to 100)
49550      */
49551     height : 100,
49552     /**
49553      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
49554      */
49555     allowBlank : false,
49556     
49557     //private
49558     // {Object} signPanel The signature SVG panel element (defaults to {})
49559     signPanel : {},
49560     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
49561     isMouseDown : false,
49562     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
49563     isConfirmed : false,
49564     // {String} signatureTmp SVG mapping string (defaults to empty string)
49565     signatureTmp : '',
49566     
49567     
49568     defaultAutoCreate : { // modified by initCompnoent..
49569         tag: "input",
49570         type:"hidden"
49571     },
49572
49573     // private
49574     onRender : function(ct, position){
49575         
49576         Roo.form.Signature.superclass.onRender.call(this, ct, position);
49577         
49578         this.wrap = this.el.wrap({
49579             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
49580         });
49581         
49582         this.createToolbar(this);
49583         this.signPanel = this.wrap.createChild({
49584                 tag: 'div',
49585                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
49586             }, this.el
49587         );
49588             
49589         this.svgID = Roo.id();
49590         this.svgEl = this.signPanel.createChild({
49591               xmlns : 'http://www.w3.org/2000/svg',
49592               tag : 'svg',
49593               id : this.svgID + "-svg",
49594               width: this.width,
49595               height: this.height,
49596               viewBox: '0 0 '+this.width+' '+this.height,
49597               cn : [
49598                 {
49599                     tag: "rect",
49600                     id: this.svgID + "-svg-r",
49601                     width: this.width,
49602                     height: this.height,
49603                     fill: "#ffa"
49604                 },
49605                 {
49606                     tag: "line",
49607                     id: this.svgID + "-svg-l",
49608                     x1: "0", // start
49609                     y1: (this.height*0.8), // start set the line in 80% of height
49610                     x2: this.width, // end
49611                     y2: (this.height*0.8), // end set the line in 80% of height
49612                     'stroke': "#666",
49613                     'stroke-width': "1",
49614                     'stroke-dasharray': "3",
49615                     'shape-rendering': "crispEdges",
49616                     'pointer-events': "none"
49617                 },
49618                 {
49619                     tag: "path",
49620                     id: this.svgID + "-svg-p",
49621                     'stroke': "navy",
49622                     'stroke-width': "3",
49623                     'fill': "none",
49624                     'pointer-events': 'none'
49625                 }
49626               ]
49627         });
49628         this.createSVG();
49629         this.svgBox = this.svgEl.dom.getScreenCTM();
49630     },
49631     createSVG : function(){ 
49632         var svg = this.signPanel;
49633         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
49634         var t = this;
49635
49636         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
49637         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
49638         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
49639         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
49640         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
49641         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
49642         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
49643         
49644     },
49645     isTouchEvent : function(e){
49646         return e.type.match(/^touch/);
49647     },
49648     getCoords : function (e) {
49649         var pt    = this.svgEl.dom.createSVGPoint();
49650         pt.x = e.clientX; 
49651         pt.y = e.clientY;
49652         if (this.isTouchEvent(e)) {
49653             pt.x =  e.targetTouches[0].clientX;
49654             pt.y = e.targetTouches[0].clientY;
49655         }
49656         var a = this.svgEl.dom.getScreenCTM();
49657         var b = a.inverse();
49658         var mx = pt.matrixTransform(b);
49659         return mx.x + ',' + mx.y;
49660     },
49661     //mouse event headler 
49662     down : function (e) {
49663         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
49664         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
49665         
49666         this.isMouseDown = true;
49667         
49668         e.preventDefault();
49669     },
49670     move : function (e) {
49671         if (this.isMouseDown) {
49672             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
49673             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
49674         }
49675         
49676         e.preventDefault();
49677     },
49678     up : function (e) {
49679         this.isMouseDown = false;
49680         var sp = this.signatureTmp.split(' ');
49681         
49682         if(sp.length > 1){
49683             if(!sp[sp.length-2].match(/^L/)){
49684                 sp.pop();
49685                 sp.pop();
49686                 sp.push("");
49687                 this.signatureTmp = sp.join(" ");
49688             }
49689         }
49690         if(this.getValue() != this.signatureTmp){
49691             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49692             this.isConfirmed = false;
49693         }
49694         e.preventDefault();
49695     },
49696     
49697     /**
49698      * Protected method that will not generally be called directly. It
49699      * is called when the editor creates its toolbar. Override this method if you need to
49700      * add custom toolbar buttons.
49701      * @param {HtmlEditor} editor
49702      */
49703     createToolbar : function(editor){
49704          function btn(id, toggle, handler){
49705             var xid = fid + '-'+ id ;
49706             return {
49707                 id : xid,
49708                 cmd : id,
49709                 cls : 'x-btn-icon x-edit-'+id,
49710                 enableToggle:toggle !== false,
49711                 scope: editor, // was editor...
49712                 handler:handler||editor.relayBtnCmd,
49713                 clickEvent:'mousedown',
49714                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49715                 tabIndex:-1
49716             };
49717         }
49718         
49719         
49720         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
49721         this.tb = tb;
49722         this.tb.add(
49723            {
49724                 cls : ' x-signature-btn x-signature-'+id,
49725                 scope: editor, // was editor...
49726                 handler: this.reset,
49727                 clickEvent:'mousedown',
49728                 text: this.labels.clear
49729             },
49730             {
49731                  xtype : 'Fill',
49732                  xns: Roo.Toolbar
49733             }, 
49734             {
49735                 cls : '  x-signature-btn x-signature-'+id,
49736                 scope: editor, // was editor...
49737                 handler: this.confirmHandler,
49738                 clickEvent:'mousedown',
49739                 text: this.labels.confirm
49740             }
49741         );
49742     
49743     },
49744     //public
49745     /**
49746      * when user is clicked confirm then show this image.....
49747      * 
49748      * @return {String} Image Data URI
49749      */
49750     getImageDataURI : function(){
49751         var svg = this.svgEl.dom.parentNode.innerHTML;
49752         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
49753         return src; 
49754     },
49755     /**
49756      * 
49757      * @return {Boolean} this.isConfirmed
49758      */
49759     getConfirmed : function(){
49760         return this.isConfirmed;
49761     },
49762     /**
49763      * 
49764      * @return {Number} this.width
49765      */
49766     getWidth : function(){
49767         return this.width;
49768     },
49769     /**
49770      * 
49771      * @return {Number} this.height
49772      */
49773     getHeight : function(){
49774         return this.height;
49775     },
49776     // private
49777     getSignature : function(){
49778         return this.signatureTmp;
49779     },
49780     // private
49781     reset : function(){
49782         this.signatureTmp = '';
49783         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49784         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
49785         this.isConfirmed = false;
49786         Roo.form.Signature.superclass.reset.call(this);
49787     },
49788     setSignature : function(s){
49789         this.signatureTmp = s;
49790         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49791         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
49792         this.setValue(s);
49793         this.isConfirmed = false;
49794         Roo.form.Signature.superclass.reset.call(this);
49795     }, 
49796     test : function(){
49797 //        Roo.log(this.signPanel.dom.contentWindow.up())
49798     },
49799     //private
49800     setConfirmed : function(){
49801         
49802         
49803         
49804 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
49805     },
49806     // private
49807     confirmHandler : function(){
49808         if(!this.getSignature()){
49809             return;
49810         }
49811         
49812         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
49813         this.setValue(this.getSignature());
49814         this.isConfirmed = true;
49815         
49816         this.fireEvent('confirm', this);
49817     },
49818     // private
49819     // Subclasses should provide the validation implementation by overriding this
49820     validateValue : function(value){
49821         if(this.allowBlank){
49822             return true;
49823         }
49824         
49825         if(this.isConfirmed){
49826             return true;
49827         }
49828         return false;
49829     }
49830 });/*
49831  * Based on:
49832  * Ext JS Library 1.1.1
49833  * Copyright(c) 2006-2007, Ext JS, LLC.
49834  *
49835  * Originally Released Under LGPL - original licence link has changed is not relivant.
49836  *
49837  * Fork - LGPL
49838  * <script type="text/javascript">
49839  */
49840  
49841
49842 /**
49843  * @class Roo.form.ComboBox
49844  * @extends Roo.form.TriggerField
49845  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
49846  * @constructor
49847  * Create a new ComboBox.
49848  * @param {Object} config Configuration options
49849  */
49850 Roo.form.Select = function(config){
49851     Roo.form.Select.superclass.constructor.call(this, config);
49852      
49853 };
49854
49855 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
49856     /**
49857      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
49858      */
49859     /**
49860      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
49861      * rendering into an Roo.Editor, defaults to false)
49862      */
49863     /**
49864      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
49865      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
49866      */
49867     /**
49868      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
49869      */
49870     /**
49871      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
49872      * the dropdown list (defaults to undefined, with no header element)
49873      */
49874
49875      /**
49876      * @cfg {String/Roo.Template} tpl The template to use to render the output
49877      */
49878      
49879     // private
49880     defaultAutoCreate : {tag: "select"  },
49881     /**
49882      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
49883      */
49884     listWidth: undefined,
49885     /**
49886      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
49887      * mode = 'remote' or 'text' if mode = 'local')
49888      */
49889     displayField: undefined,
49890     /**
49891      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
49892      * mode = 'remote' or 'value' if mode = 'local'). 
49893      * Note: use of a valueField requires the user make a selection
49894      * in order for a value to be mapped.
49895      */
49896     valueField: undefined,
49897     
49898     
49899     /**
49900      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
49901      * field's data value (defaults to the underlying DOM element's name)
49902      */
49903     hiddenName: undefined,
49904     /**
49905      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
49906      */
49907     listClass: '',
49908     /**
49909      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
49910      */
49911     selectedClass: 'x-combo-selected',
49912     /**
49913      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
49914      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
49915      * which displays a downward arrow icon).
49916      */
49917     triggerClass : 'x-form-arrow-trigger',
49918     /**
49919      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
49920      */
49921     shadow:'sides',
49922     /**
49923      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
49924      * anchor positions (defaults to 'tl-bl')
49925      */
49926     listAlign: 'tl-bl?',
49927     /**
49928      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
49929      */
49930     maxHeight: 300,
49931     /**
49932      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
49933      * query specified by the allQuery config option (defaults to 'query')
49934      */
49935     triggerAction: 'query',
49936     /**
49937      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
49938      * (defaults to 4, does not apply if editable = false)
49939      */
49940     minChars : 4,
49941     /**
49942      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
49943      * delay (typeAheadDelay) if it matches a known value (defaults to false)
49944      */
49945     typeAhead: false,
49946     /**
49947      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
49948      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
49949      */
49950     queryDelay: 500,
49951     /**
49952      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
49953      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
49954      */
49955     pageSize: 0,
49956     /**
49957      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
49958      * when editable = true (defaults to false)
49959      */
49960     selectOnFocus:false,
49961     /**
49962      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
49963      */
49964     queryParam: 'query',
49965     /**
49966      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
49967      * when mode = 'remote' (defaults to 'Loading...')
49968      */
49969     loadingText: 'Loading...',
49970     /**
49971      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
49972      */
49973     resizable: false,
49974     /**
49975      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
49976      */
49977     handleHeight : 8,
49978     /**
49979      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
49980      * traditional select (defaults to true)
49981      */
49982     editable: true,
49983     /**
49984      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
49985      */
49986     allQuery: '',
49987     /**
49988      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
49989      */
49990     mode: 'remote',
49991     /**
49992      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
49993      * listWidth has a higher value)
49994      */
49995     minListWidth : 70,
49996     /**
49997      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
49998      * allow the user to set arbitrary text into the field (defaults to false)
49999      */
50000     forceSelection:false,
50001     /**
50002      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
50003      * if typeAhead = true (defaults to 250)
50004      */
50005     typeAheadDelay : 250,
50006     /**
50007      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
50008      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
50009      */
50010     valueNotFoundText : undefined,
50011     
50012     /**
50013      * @cfg {String} defaultValue The value displayed after loading the store.
50014      */
50015     defaultValue: '',
50016     
50017     /**
50018      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
50019      */
50020     blockFocus : false,
50021     
50022     /**
50023      * @cfg {Boolean} disableClear Disable showing of clear button.
50024      */
50025     disableClear : false,
50026     /**
50027      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
50028      */
50029     alwaysQuery : false,
50030     
50031     //private
50032     addicon : false,
50033     editicon: false,
50034     
50035     // element that contains real text value.. (when hidden is used..)
50036      
50037     // private
50038     onRender : function(ct, position){
50039         Roo.form.Field.prototype.onRender.call(this, ct, position);
50040         
50041         if(this.store){
50042             this.store.on('beforeload', this.onBeforeLoad, this);
50043             this.store.on('load', this.onLoad, this);
50044             this.store.on('loadexception', this.onLoadException, this);
50045             this.store.load({});
50046         }
50047         
50048         
50049         
50050     },
50051
50052     // private
50053     initEvents : function(){
50054         //Roo.form.ComboBox.superclass.initEvents.call(this);
50055  
50056     },
50057
50058     onDestroy : function(){
50059        
50060         if(this.store){
50061             this.store.un('beforeload', this.onBeforeLoad, this);
50062             this.store.un('load', this.onLoad, this);
50063             this.store.un('loadexception', this.onLoadException, this);
50064         }
50065         //Roo.form.ComboBox.superclass.onDestroy.call(this);
50066     },
50067
50068     // private
50069     fireKey : function(e){
50070         if(e.isNavKeyPress() && !this.list.isVisible()){
50071             this.fireEvent("specialkey", this, e);
50072         }
50073     },
50074
50075     // private
50076     onResize: function(w, h){
50077         
50078         return; 
50079     
50080         
50081     },
50082
50083     /**
50084      * Allow or prevent the user from directly editing the field text.  If false is passed,
50085      * the user will only be able to select from the items defined in the dropdown list.  This method
50086      * is the runtime equivalent of setting the 'editable' config option at config time.
50087      * @param {Boolean} value True to allow the user to directly edit the field text
50088      */
50089     setEditable : function(value){
50090          
50091     },
50092
50093     // private
50094     onBeforeLoad : function(){
50095         
50096         Roo.log("Select before load");
50097         return;
50098     
50099         this.innerList.update(this.loadingText ?
50100                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
50101         //this.restrictHeight();
50102         this.selectedIndex = -1;
50103     },
50104
50105     // private
50106     onLoad : function(){
50107
50108     
50109         var dom = this.el.dom;
50110         dom.innerHTML = '';
50111          var od = dom.ownerDocument;
50112          
50113         if (this.emptyText) {
50114             var op = od.createElement('option');
50115             op.setAttribute('value', '');
50116             op.innerHTML = String.format('{0}', this.emptyText);
50117             dom.appendChild(op);
50118         }
50119         if(this.store.getCount() > 0){
50120            
50121             var vf = this.valueField;
50122             var df = this.displayField;
50123             this.store.data.each(function(r) {
50124                 // which colmsn to use... testing - cdoe / title..
50125                 var op = od.createElement('option');
50126                 op.setAttribute('value', r.data[vf]);
50127                 op.innerHTML = String.format('{0}', r.data[df]);
50128                 dom.appendChild(op);
50129             });
50130             if (typeof(this.defaultValue != 'undefined')) {
50131                 this.setValue(this.defaultValue);
50132             }
50133             
50134              
50135         }else{
50136             //this.onEmptyResults();
50137         }
50138         //this.el.focus();
50139     },
50140     // private
50141     onLoadException : function()
50142     {
50143         dom.innerHTML = '';
50144             
50145         Roo.log("Select on load exception");
50146         return;
50147     
50148         this.collapse();
50149         Roo.log(this.store.reader.jsonData);
50150         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
50151             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
50152         }
50153         
50154         
50155     },
50156     // private
50157     onTypeAhead : function(){
50158          
50159     },
50160
50161     // private
50162     onSelect : function(record, index){
50163         Roo.log('on select?');
50164         return;
50165         if(this.fireEvent('beforeselect', this, record, index) !== false){
50166             this.setFromData(index > -1 ? record.data : false);
50167             this.collapse();
50168             this.fireEvent('select', this, record, index);
50169         }
50170     },
50171
50172     /**
50173      * Returns the currently selected field value or empty string if no value is set.
50174      * @return {String} value The selected value
50175      */
50176     getValue : function(){
50177         var dom = this.el.dom;
50178         this.value = dom.options[dom.selectedIndex].value;
50179         return this.value;
50180         
50181     },
50182
50183     /**
50184      * Clears any text/value currently set in the field
50185      */
50186     clearValue : function(){
50187         this.value = '';
50188         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
50189         
50190     },
50191
50192     /**
50193      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
50194      * will be displayed in the field.  If the value does not match the data value of an existing item,
50195      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
50196      * Otherwise the field will be blank (although the value will still be set).
50197      * @param {String} value The value to match
50198      */
50199     setValue : function(v){
50200         var d = this.el.dom;
50201         for (var i =0; i < d.options.length;i++) {
50202             if (v == d.options[i].value) {
50203                 d.selectedIndex = i;
50204                 this.value = v;
50205                 return;
50206             }
50207         }
50208         this.clearValue();
50209     },
50210     /**
50211      * @property {Object} the last set data for the element
50212      */
50213     
50214     lastData : false,
50215     /**
50216      * Sets the value of the field based on a object which is related to the record format for the store.
50217      * @param {Object} value the value to set as. or false on reset?
50218      */
50219     setFromData : function(o){
50220         Roo.log('setfrom data?');
50221          
50222         
50223         
50224     },
50225     // private
50226     reset : function(){
50227         this.clearValue();
50228     },
50229     // private
50230     findRecord : function(prop, value){
50231         
50232         return false;
50233     
50234         var record;
50235         if(this.store.getCount() > 0){
50236             this.store.each(function(r){
50237                 if(r.data[prop] == value){
50238                     record = r;
50239                     return false;
50240                 }
50241                 return true;
50242             });
50243         }
50244         return record;
50245     },
50246     
50247     getName: function()
50248     {
50249         // returns hidden if it's set..
50250         if (!this.rendered) {return ''};
50251         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
50252         
50253     },
50254      
50255
50256     
50257
50258     // private
50259     onEmptyResults : function(){
50260         Roo.log('empty results');
50261         //this.collapse();
50262     },
50263
50264     /**
50265      * Returns true if the dropdown list is expanded, else false.
50266      */
50267     isExpanded : function(){
50268         return false;
50269     },
50270
50271     /**
50272      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
50273      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50274      * @param {String} value The data value of the item to select
50275      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50276      * selected item if it is not currently in view (defaults to true)
50277      * @return {Boolean} True if the value matched an item in the list, else false
50278      */
50279     selectByValue : function(v, scrollIntoView){
50280         Roo.log('select By Value');
50281         return false;
50282     
50283         if(v !== undefined && v !== null){
50284             var r = this.findRecord(this.valueField || this.displayField, v);
50285             if(r){
50286                 this.select(this.store.indexOf(r), scrollIntoView);
50287                 return true;
50288             }
50289         }
50290         return false;
50291     },
50292
50293     /**
50294      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
50295      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50296      * @param {Number} index The zero-based index of the list item to select
50297      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50298      * selected item if it is not currently in view (defaults to true)
50299      */
50300     select : function(index, scrollIntoView){
50301         Roo.log('select ');
50302         return  ;
50303         
50304         this.selectedIndex = index;
50305         this.view.select(index);
50306         if(scrollIntoView !== false){
50307             var el = this.view.getNode(index);
50308             if(el){
50309                 this.innerList.scrollChildIntoView(el, false);
50310             }
50311         }
50312     },
50313
50314       
50315
50316     // private
50317     validateBlur : function(){
50318         
50319         return;
50320         
50321     },
50322
50323     // private
50324     initQuery : function(){
50325         this.doQuery(this.getRawValue());
50326     },
50327
50328     // private
50329     doForce : function(){
50330         if(this.el.dom.value.length > 0){
50331             this.el.dom.value =
50332                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
50333              
50334         }
50335     },
50336
50337     /**
50338      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
50339      * query allowing the query action to be canceled if needed.
50340      * @param {String} query The SQL query to execute
50341      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
50342      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
50343      * saved in the current store (defaults to false)
50344      */
50345     doQuery : function(q, forceAll){
50346         
50347         Roo.log('doQuery?');
50348         if(q === undefined || q === null){
50349             q = '';
50350         }
50351         var qe = {
50352             query: q,
50353             forceAll: forceAll,
50354             combo: this,
50355             cancel:false
50356         };
50357         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
50358             return false;
50359         }
50360         q = qe.query;
50361         forceAll = qe.forceAll;
50362         if(forceAll === true || (q.length >= this.minChars)){
50363             if(this.lastQuery != q || this.alwaysQuery){
50364                 this.lastQuery = q;
50365                 if(this.mode == 'local'){
50366                     this.selectedIndex = -1;
50367                     if(forceAll){
50368                         this.store.clearFilter();
50369                     }else{
50370                         this.store.filter(this.displayField, q);
50371                     }
50372                     this.onLoad();
50373                 }else{
50374                     this.store.baseParams[this.queryParam] = q;
50375                     this.store.load({
50376                         params: this.getParams(q)
50377                     });
50378                     this.expand();
50379                 }
50380             }else{
50381                 this.selectedIndex = -1;
50382                 this.onLoad();   
50383             }
50384         }
50385     },
50386
50387     // private
50388     getParams : function(q){
50389         var p = {};
50390         //p[this.queryParam] = q;
50391         if(this.pageSize){
50392             p.start = 0;
50393             p.limit = this.pageSize;
50394         }
50395         return p;
50396     },
50397
50398     /**
50399      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
50400      */
50401     collapse : function(){
50402         
50403     },
50404
50405     // private
50406     collapseIf : function(e){
50407         
50408     },
50409
50410     /**
50411      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
50412      */
50413     expand : function(){
50414         
50415     } ,
50416
50417     // private
50418      
50419
50420     /** 
50421     * @cfg {Boolean} grow 
50422     * @hide 
50423     */
50424     /** 
50425     * @cfg {Number} growMin 
50426     * @hide 
50427     */
50428     /** 
50429     * @cfg {Number} growMax 
50430     * @hide 
50431     */
50432     /**
50433      * @hide
50434      * @method autoSize
50435      */
50436     
50437     setWidth : function()
50438     {
50439         
50440     },
50441     getResizeEl : function(){
50442         return this.el;
50443     }
50444 });//<script type="text/javasscript">
50445  
50446
50447 /**
50448  * @class Roo.DDView
50449  * A DnD enabled version of Roo.View.
50450  * @param {Element/String} container The Element in which to create the View.
50451  * @param {String} tpl The template string used to create the markup for each element of the View
50452  * @param {Object} config The configuration properties. These include all the config options of
50453  * {@link Roo.View} plus some specific to this class.<br>
50454  * <p>
50455  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
50456  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
50457  * <p>
50458  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
50459 .x-view-drag-insert-above {
50460         border-top:1px dotted #3366cc;
50461 }
50462 .x-view-drag-insert-below {
50463         border-bottom:1px dotted #3366cc;
50464 }
50465 </code></pre>
50466  * 
50467  */
50468  
50469 Roo.DDView = function(container, tpl, config) {
50470     Roo.DDView.superclass.constructor.apply(this, arguments);
50471     this.getEl().setStyle("outline", "0px none");
50472     this.getEl().unselectable();
50473     if (this.dragGroup) {
50474                 this.setDraggable(this.dragGroup.split(","));
50475     }
50476     if (this.dropGroup) {
50477                 this.setDroppable(this.dropGroup.split(","));
50478     }
50479     if (this.deletable) {
50480         this.setDeletable();
50481     }
50482     this.isDirtyFlag = false;
50483         this.addEvents({
50484                 "drop" : true
50485         });
50486 };
50487
50488 Roo.extend(Roo.DDView, Roo.View, {
50489 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
50490 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
50491 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
50492 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
50493
50494         isFormField: true,
50495
50496         reset: Roo.emptyFn,
50497         
50498         clearInvalid: Roo.form.Field.prototype.clearInvalid,
50499
50500         validate: function() {
50501                 return true;
50502         },
50503         
50504         destroy: function() {
50505                 this.purgeListeners();
50506                 this.getEl.removeAllListeners();
50507                 this.getEl().remove();
50508                 if (this.dragZone) {
50509                         if (this.dragZone.destroy) {
50510                                 this.dragZone.destroy();
50511                         }
50512                 }
50513                 if (this.dropZone) {
50514                         if (this.dropZone.destroy) {
50515                                 this.dropZone.destroy();
50516                         }
50517                 }
50518         },
50519
50520 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
50521         getName: function() {
50522                 return this.name;
50523         },
50524
50525 /**     Loads the View from a JSON string representing the Records to put into the Store. */
50526         setValue: function(v) {
50527                 if (!this.store) {
50528                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
50529                 }
50530                 var data = {};
50531                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
50532                 this.store.proxy = new Roo.data.MemoryProxy(data);
50533                 this.store.load();
50534         },
50535
50536 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
50537         getValue: function() {
50538                 var result = '(';
50539                 this.store.each(function(rec) {
50540                         result += rec.id + ',';
50541                 });
50542                 return result.substr(0, result.length - 1) + ')';
50543         },
50544         
50545         getIds: function() {
50546                 var i = 0, result = new Array(this.store.getCount());
50547                 this.store.each(function(rec) {
50548                         result[i++] = rec.id;
50549                 });
50550                 return result;
50551         },
50552         
50553         isDirty: function() {
50554                 return this.isDirtyFlag;
50555         },
50556
50557 /**
50558  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
50559  *      whole Element becomes the target, and this causes the drop gesture to append.
50560  */
50561     getTargetFromEvent : function(e) {
50562                 var target = e.getTarget();
50563                 while ((target !== null) && (target.parentNode != this.el.dom)) {
50564                 target = target.parentNode;
50565                 }
50566                 if (!target) {
50567                         target = this.el.dom.lastChild || this.el.dom;
50568                 }
50569                 return target;
50570     },
50571
50572 /**
50573  *      Create the drag data which consists of an object which has the property "ddel" as
50574  *      the drag proxy element. 
50575  */
50576     getDragData : function(e) {
50577         var target = this.findItemFromChild(e.getTarget());
50578                 if(target) {
50579                         this.handleSelection(e);
50580                         var selNodes = this.getSelectedNodes();
50581             var dragData = {
50582                 source: this,
50583                 copy: this.copy || (this.allowCopy && e.ctrlKey),
50584                 nodes: selNodes,
50585                 records: []
50586                         };
50587                         var selectedIndices = this.getSelectedIndexes();
50588                         for (var i = 0; i < selectedIndices.length; i++) {
50589                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
50590                         }
50591                         if (selNodes.length == 1) {
50592                                 dragData.ddel = target.cloneNode(true); // the div element
50593                         } else {
50594                                 var div = document.createElement('div'); // create the multi element drag "ghost"
50595                                 div.className = 'multi-proxy';
50596                                 for (var i = 0, len = selNodes.length; i < len; i++) {
50597                                         div.appendChild(selNodes[i].cloneNode(true));
50598                                 }
50599                                 dragData.ddel = div;
50600                         }
50601             //console.log(dragData)
50602             //console.log(dragData.ddel.innerHTML)
50603                         return dragData;
50604                 }
50605         //console.log('nodragData')
50606                 return false;
50607     },
50608     
50609 /**     Specify to which ddGroup items in this DDView may be dragged. */
50610     setDraggable: function(ddGroup) {
50611         if (ddGroup instanceof Array) {
50612                 Roo.each(ddGroup, this.setDraggable, this);
50613                 return;
50614         }
50615         if (this.dragZone) {
50616                 this.dragZone.addToGroup(ddGroup);
50617         } else {
50618                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
50619                                 containerScroll: true,
50620                                 ddGroup: ddGroup 
50621
50622                         });
50623 //                      Draggability implies selection. DragZone's mousedown selects the element.
50624                         if (!this.multiSelect) { this.singleSelect = true; }
50625
50626 //                      Wire the DragZone's handlers up to methods in *this*
50627                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
50628                 }
50629     },
50630
50631 /**     Specify from which ddGroup this DDView accepts drops. */
50632     setDroppable: function(ddGroup) {
50633         if (ddGroup instanceof Array) {
50634                 Roo.each(ddGroup, this.setDroppable, this);
50635                 return;
50636         }
50637         if (this.dropZone) {
50638                 this.dropZone.addToGroup(ddGroup);
50639         } else {
50640                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
50641                                 containerScroll: true,
50642                                 ddGroup: ddGroup
50643                         });
50644
50645 //                      Wire the DropZone's handlers up to methods in *this*
50646                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
50647                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
50648                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
50649                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
50650                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
50651                 }
50652     },
50653
50654 /**     Decide whether to drop above or below a View node. */
50655     getDropPoint : function(e, n, dd){
50656         if (n == this.el.dom) { return "above"; }
50657                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
50658                 var c = t + (b - t) / 2;
50659                 var y = Roo.lib.Event.getPageY(e);
50660                 if(y <= c) {
50661                         return "above";
50662                 }else{
50663                         return "below";
50664                 }
50665     },
50666
50667     onNodeEnter : function(n, dd, e, data){
50668                 return false;
50669     },
50670     
50671     onNodeOver : function(n, dd, e, data){
50672                 var pt = this.getDropPoint(e, n, dd);
50673                 // set the insert point style on the target node
50674                 var dragElClass = this.dropNotAllowed;
50675                 if (pt) {
50676                         var targetElClass;
50677                         if (pt == "above"){
50678                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
50679                                 targetElClass = "x-view-drag-insert-above";
50680                         } else {
50681                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
50682                                 targetElClass = "x-view-drag-insert-below";
50683                         }
50684                         if (this.lastInsertClass != targetElClass){
50685                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
50686                                 this.lastInsertClass = targetElClass;
50687                         }
50688                 }
50689                 return dragElClass;
50690         },
50691
50692     onNodeOut : function(n, dd, e, data){
50693                 this.removeDropIndicators(n);
50694     },
50695
50696     onNodeDrop : function(n, dd, e, data){
50697         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
50698                 return false;
50699         }
50700         var pt = this.getDropPoint(e, n, dd);
50701                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
50702                 if (pt == "below") { insertAt++; }
50703                 for (var i = 0; i < data.records.length; i++) {
50704                         var r = data.records[i];
50705                         var dup = this.store.getById(r.id);
50706                         if (dup && (dd != this.dragZone)) {
50707                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
50708                         } else {
50709                                 if (data.copy) {
50710                                         this.store.insert(insertAt++, r.copy());
50711                                 } else {
50712                                         data.source.isDirtyFlag = true;
50713                                         r.store.remove(r);
50714                                         this.store.insert(insertAt++, r);
50715                                 }
50716                                 this.isDirtyFlag = true;
50717                         }
50718                 }
50719                 this.dragZone.cachedTarget = null;
50720                 return true;
50721     },
50722
50723     removeDropIndicators : function(n){
50724                 if(n){
50725                         Roo.fly(n).removeClass([
50726                                 "x-view-drag-insert-above",
50727                                 "x-view-drag-insert-below"]);
50728                         this.lastInsertClass = "_noclass";
50729                 }
50730     },
50731
50732 /**
50733  *      Utility method. Add a delete option to the DDView's context menu.
50734  *      @param {String} imageUrl The URL of the "delete" icon image.
50735  */
50736         setDeletable: function(imageUrl) {
50737                 if (!this.singleSelect && !this.multiSelect) {
50738                         this.singleSelect = true;
50739                 }
50740                 var c = this.getContextMenu();
50741                 this.contextMenu.on("itemclick", function(item) {
50742                         switch (item.id) {
50743                                 case "delete":
50744                                         this.remove(this.getSelectedIndexes());
50745                                         break;
50746                         }
50747                 }, this);
50748                 this.contextMenu.add({
50749                         icon: imageUrl,
50750                         id: "delete",
50751                         text: 'Delete'
50752                 });
50753         },
50754         
50755 /**     Return the context menu for this DDView. */
50756         getContextMenu: function() {
50757                 if (!this.contextMenu) {
50758 //                      Create the View's context menu
50759                         this.contextMenu = new Roo.menu.Menu({
50760                                 id: this.id + "-contextmenu"
50761                         });
50762                         this.el.on("contextmenu", this.showContextMenu, this);
50763                 }
50764                 return this.contextMenu;
50765         },
50766         
50767         disableContextMenu: function() {
50768                 if (this.contextMenu) {
50769                         this.el.un("contextmenu", this.showContextMenu, this);
50770                 }
50771         },
50772
50773         showContextMenu: function(e, item) {
50774         item = this.findItemFromChild(e.getTarget());
50775                 if (item) {
50776                         e.stopEvent();
50777                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
50778                         this.contextMenu.showAt(e.getXY());
50779             }
50780     },
50781
50782 /**
50783  *      Remove {@link Roo.data.Record}s at the specified indices.
50784  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
50785  */
50786     remove: function(selectedIndices) {
50787                 selectedIndices = [].concat(selectedIndices);
50788                 for (var i = 0; i < selectedIndices.length; i++) {
50789                         var rec = this.store.getAt(selectedIndices[i]);
50790                         this.store.remove(rec);
50791                 }
50792     },
50793
50794 /**
50795  *      Double click fires the event, but also, if this is draggable, and there is only one other
50796  *      related DropZone, it transfers the selected node.
50797  */
50798     onDblClick : function(e){
50799         var item = this.findItemFromChild(e.getTarget());
50800         if(item){
50801             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
50802                 return false;
50803             }
50804             if (this.dragGroup) {
50805                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
50806                     while (targets.indexOf(this.dropZone) > -1) {
50807                             targets.remove(this.dropZone);
50808                                 }
50809                     if (targets.length == 1) {
50810                                         this.dragZone.cachedTarget = null;
50811                         var el = Roo.get(targets[0].getEl());
50812                         var box = el.getBox(true);
50813                         targets[0].onNodeDrop(el.dom, {
50814                                 target: el.dom,
50815                                 xy: [box.x, box.y + box.height - 1]
50816                         }, null, this.getDragData(e));
50817                     }
50818                 }
50819         }
50820     },
50821     
50822     handleSelection: function(e) {
50823                 this.dragZone.cachedTarget = null;
50824         var item = this.findItemFromChild(e.getTarget());
50825         if (!item) {
50826                 this.clearSelections(true);
50827                 return;
50828         }
50829                 if (item && (this.multiSelect || this.singleSelect)){
50830                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
50831                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
50832                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
50833                                 this.unselect(item);
50834                         } else {
50835                                 this.select(item, this.multiSelect && e.ctrlKey);
50836                                 this.lastSelection = item;
50837                         }
50838                 }
50839     },
50840
50841     onItemClick : function(item, index, e){
50842                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
50843                         return false;
50844                 }
50845                 return true;
50846     },
50847
50848     unselect : function(nodeInfo, suppressEvent){
50849                 var node = this.getNode(nodeInfo);
50850                 if(node && this.isSelected(node)){
50851                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
50852                                 Roo.fly(node).removeClass(this.selectedClass);
50853                                 this.selections.remove(node);
50854                                 if(!suppressEvent){
50855                                         this.fireEvent("selectionchange", this, this.selections);
50856                                 }
50857                         }
50858                 }
50859     }
50860 });
50861 /*
50862  * Based on:
50863  * Ext JS Library 1.1.1
50864  * Copyright(c) 2006-2007, Ext JS, LLC.
50865  *
50866  * Originally Released Under LGPL - original licence link has changed is not relivant.
50867  *
50868  * Fork - LGPL
50869  * <script type="text/javascript">
50870  */
50871  
50872 /**
50873  * @class Roo.LayoutManager
50874  * @extends Roo.util.Observable
50875  * Base class for layout managers.
50876  */
50877 Roo.LayoutManager = function(container, config){
50878     Roo.LayoutManager.superclass.constructor.call(this);
50879     this.el = Roo.get(container);
50880     // ie scrollbar fix
50881     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
50882         document.body.scroll = "no";
50883     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
50884         this.el.position('relative');
50885     }
50886     this.id = this.el.id;
50887     this.el.addClass("x-layout-container");
50888     /** false to disable window resize monitoring @type Boolean */
50889     this.monitorWindowResize = true;
50890     this.regions = {};
50891     this.addEvents({
50892         /**
50893          * @event layout
50894          * Fires when a layout is performed. 
50895          * @param {Roo.LayoutManager} this
50896          */
50897         "layout" : true,
50898         /**
50899          * @event regionresized
50900          * Fires when the user resizes a region. 
50901          * @param {Roo.LayoutRegion} region The resized region
50902          * @param {Number} newSize The new size (width for east/west, height for north/south)
50903          */
50904         "regionresized" : true,
50905         /**
50906          * @event regioncollapsed
50907          * Fires when a region is collapsed. 
50908          * @param {Roo.LayoutRegion} region The collapsed region
50909          */
50910         "regioncollapsed" : true,
50911         /**
50912          * @event regionexpanded
50913          * Fires when a region is expanded.  
50914          * @param {Roo.LayoutRegion} region The expanded region
50915          */
50916         "regionexpanded" : true
50917     });
50918     this.updating = false;
50919     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
50920 };
50921
50922 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
50923     /**
50924      * Returns true if this layout is currently being updated
50925      * @return {Boolean}
50926      */
50927     isUpdating : function(){
50928         return this.updating; 
50929     },
50930     
50931     /**
50932      * Suspend the LayoutManager from doing auto-layouts while
50933      * making multiple add or remove calls
50934      */
50935     beginUpdate : function(){
50936         this.updating = true;    
50937     },
50938     
50939     /**
50940      * Restore auto-layouts and optionally disable the manager from performing a layout
50941      * @param {Boolean} noLayout true to disable a layout update 
50942      */
50943     endUpdate : function(noLayout){
50944         this.updating = false;
50945         if(!noLayout){
50946             this.layout();
50947         }    
50948     },
50949     
50950     layout: function(){
50951         
50952     },
50953     
50954     onRegionResized : function(region, newSize){
50955         this.fireEvent("regionresized", region, newSize);
50956         this.layout();
50957     },
50958     
50959     onRegionCollapsed : function(region){
50960         this.fireEvent("regioncollapsed", region);
50961     },
50962     
50963     onRegionExpanded : function(region){
50964         this.fireEvent("regionexpanded", region);
50965     },
50966         
50967     /**
50968      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
50969      * performs box-model adjustments.
50970      * @return {Object} The size as an object {width: (the width), height: (the height)}
50971      */
50972     getViewSize : function(){
50973         var size;
50974         if(this.el.dom != document.body){
50975             size = this.el.getSize();
50976         }else{
50977             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
50978         }
50979         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
50980         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
50981         return size;
50982     },
50983     
50984     /**
50985      * Returns the Element this layout is bound to.
50986      * @return {Roo.Element}
50987      */
50988     getEl : function(){
50989         return this.el;
50990     },
50991     
50992     /**
50993      * Returns the specified region.
50994      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
50995      * @return {Roo.LayoutRegion}
50996      */
50997     getRegion : function(target){
50998         return this.regions[target.toLowerCase()];
50999     },
51000     
51001     onWindowResize : function(){
51002         if(this.monitorWindowResize){
51003             this.layout();
51004         }
51005     }
51006 });/*
51007  * Based on:
51008  * Ext JS Library 1.1.1
51009  * Copyright(c) 2006-2007, Ext JS, LLC.
51010  *
51011  * Originally Released Under LGPL - original licence link has changed is not relivant.
51012  *
51013  * Fork - LGPL
51014  * <script type="text/javascript">
51015  */
51016 /**
51017  * @class Roo.BorderLayout
51018  * @extends Roo.LayoutManager
51019  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
51020  * please see: <br><br>
51021  * <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>
51022  * <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>
51023  * Example:
51024  <pre><code>
51025  var layout = new Roo.BorderLayout(document.body, {
51026     north: {
51027         initialSize: 25,
51028         titlebar: false
51029     },
51030     west: {
51031         split:true,
51032         initialSize: 200,
51033         minSize: 175,
51034         maxSize: 400,
51035         titlebar: true,
51036         collapsible: true
51037     },
51038     east: {
51039         split:true,
51040         initialSize: 202,
51041         minSize: 175,
51042         maxSize: 400,
51043         titlebar: true,
51044         collapsible: true
51045     },
51046     south: {
51047         split:true,
51048         initialSize: 100,
51049         minSize: 100,
51050         maxSize: 200,
51051         titlebar: true,
51052         collapsible: true
51053     },
51054     center: {
51055         titlebar: true,
51056         autoScroll:true,
51057         resizeTabs: true,
51058         minTabWidth: 50,
51059         preferredTabWidth: 150
51060     }
51061 });
51062
51063 // shorthand
51064 var CP = Roo.ContentPanel;
51065
51066 layout.beginUpdate();
51067 layout.add("north", new CP("north", "North"));
51068 layout.add("south", new CP("south", {title: "South", closable: true}));
51069 layout.add("west", new CP("west", {title: "West"}));
51070 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
51071 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
51072 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
51073 layout.getRegion("center").showPanel("center1");
51074 layout.endUpdate();
51075 </code></pre>
51076
51077 <b>The container the layout is rendered into can be either the body element or any other element.
51078 If it is not the body element, the container needs to either be an absolute positioned element,
51079 or you will need to add "position:relative" to the css of the container.  You will also need to specify
51080 the container size if it is not the body element.</b>
51081
51082 * @constructor
51083 * Create a new BorderLayout
51084 * @param {String/HTMLElement/Element} container The container this layout is bound to
51085 * @param {Object} config Configuration options
51086  */
51087 Roo.BorderLayout = function(container, config){
51088     config = config || {};
51089     Roo.BorderLayout.superclass.constructor.call(this, container, config);
51090     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
51091     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
51092         var target = this.factory.validRegions[i];
51093         if(config[target]){
51094             this.addRegion(target, config[target]);
51095         }
51096     }
51097 };
51098
51099 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
51100     /**
51101      * Creates and adds a new region if it doesn't already exist.
51102      * @param {String} target The target region key (north, south, east, west or center).
51103      * @param {Object} config The regions config object
51104      * @return {BorderLayoutRegion} The new region
51105      */
51106     addRegion : function(target, config){
51107         if(!this.regions[target]){
51108             var r = this.factory.create(target, this, config);
51109             this.bindRegion(target, r);
51110         }
51111         return this.regions[target];
51112     },
51113
51114     // private (kinda)
51115     bindRegion : function(name, r){
51116         this.regions[name] = r;
51117         r.on("visibilitychange", this.layout, this);
51118         r.on("paneladded", this.layout, this);
51119         r.on("panelremoved", this.layout, this);
51120         r.on("invalidated", this.layout, this);
51121         r.on("resized", this.onRegionResized, this);
51122         r.on("collapsed", this.onRegionCollapsed, this);
51123         r.on("expanded", this.onRegionExpanded, this);
51124     },
51125
51126     /**
51127      * Performs a layout update.
51128      */
51129     layout : function(){
51130         if(this.updating) {
51131             return;
51132         }
51133         var size = this.getViewSize();
51134         var w = size.width;
51135         var h = size.height;
51136         var centerW = w;
51137         var centerH = h;
51138         var centerY = 0;
51139         var centerX = 0;
51140         //var x = 0, y = 0;
51141
51142         var rs = this.regions;
51143         var north = rs["north"];
51144         var south = rs["south"]; 
51145         var west = rs["west"];
51146         var east = rs["east"];
51147         var center = rs["center"];
51148         //if(this.hideOnLayout){ // not supported anymore
51149             //c.el.setStyle("display", "none");
51150         //}
51151         if(north && north.isVisible()){
51152             var b = north.getBox();
51153             var m = north.getMargins();
51154             b.width = w - (m.left+m.right);
51155             b.x = m.left;
51156             b.y = m.top;
51157             centerY = b.height + b.y + m.bottom;
51158             centerH -= centerY;
51159             north.updateBox(this.safeBox(b));
51160         }
51161         if(south && south.isVisible()){
51162             var b = south.getBox();
51163             var m = south.getMargins();
51164             b.width = w - (m.left+m.right);
51165             b.x = m.left;
51166             var totalHeight = (b.height + m.top + m.bottom);
51167             b.y = h - totalHeight + m.top;
51168             centerH -= totalHeight;
51169             south.updateBox(this.safeBox(b));
51170         }
51171         if(west && west.isVisible()){
51172             var b = west.getBox();
51173             var m = west.getMargins();
51174             b.height = centerH - (m.top+m.bottom);
51175             b.x = m.left;
51176             b.y = centerY + m.top;
51177             var totalWidth = (b.width + m.left + m.right);
51178             centerX += totalWidth;
51179             centerW -= totalWidth;
51180             west.updateBox(this.safeBox(b));
51181         }
51182         if(east && east.isVisible()){
51183             var b = east.getBox();
51184             var m = east.getMargins();
51185             b.height = centerH - (m.top+m.bottom);
51186             var totalWidth = (b.width + m.left + m.right);
51187             b.x = w - totalWidth + m.left;
51188             b.y = centerY + m.top;
51189             centerW -= totalWidth;
51190             east.updateBox(this.safeBox(b));
51191         }
51192         if(center){
51193             var m = center.getMargins();
51194             var centerBox = {
51195                 x: centerX + m.left,
51196                 y: centerY + m.top,
51197                 width: centerW - (m.left+m.right),
51198                 height: centerH - (m.top+m.bottom)
51199             };
51200             //if(this.hideOnLayout){
51201                 //center.el.setStyle("display", "block");
51202             //}
51203             center.updateBox(this.safeBox(centerBox));
51204         }
51205         this.el.repaint();
51206         this.fireEvent("layout", this);
51207     },
51208
51209     // private
51210     safeBox : function(box){
51211         box.width = Math.max(0, box.width);
51212         box.height = Math.max(0, box.height);
51213         return box;
51214     },
51215
51216     /**
51217      * Adds a ContentPanel (or subclass) to this layout.
51218      * @param {String} target The target region key (north, south, east, west or center).
51219      * @param {Roo.ContentPanel} panel The panel to add
51220      * @return {Roo.ContentPanel} The added panel
51221      */
51222     add : function(target, panel){
51223          
51224         target = target.toLowerCase();
51225         return this.regions[target].add(panel);
51226     },
51227
51228     /**
51229      * Remove a ContentPanel (or subclass) to this layout.
51230      * @param {String} target The target region key (north, south, east, west or center).
51231      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
51232      * @return {Roo.ContentPanel} The removed panel
51233      */
51234     remove : function(target, panel){
51235         target = target.toLowerCase();
51236         return this.regions[target].remove(panel);
51237     },
51238
51239     /**
51240      * Searches all regions for a panel with the specified id
51241      * @param {String} panelId
51242      * @return {Roo.ContentPanel} The panel or null if it wasn't found
51243      */
51244     findPanel : function(panelId){
51245         var rs = this.regions;
51246         for(var target in rs){
51247             if(typeof rs[target] != "function"){
51248                 var p = rs[target].getPanel(panelId);
51249                 if(p){
51250                     return p;
51251                 }
51252             }
51253         }
51254         return null;
51255     },
51256
51257     /**
51258      * Searches all regions for a panel with the specified id and activates (shows) it.
51259      * @param {String/ContentPanel} panelId The panels id or the panel itself
51260      * @return {Roo.ContentPanel} The shown panel or null
51261      */
51262     showPanel : function(panelId) {
51263       var rs = this.regions;
51264       for(var target in rs){
51265          var r = rs[target];
51266          if(typeof r != "function"){
51267             if(r.hasPanel(panelId)){
51268                return r.showPanel(panelId);
51269             }
51270          }
51271       }
51272       return null;
51273    },
51274
51275    /**
51276      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
51277      * @param {Roo.state.Provider} provider (optional) An alternate state provider
51278      */
51279     restoreState : function(provider){
51280         if(!provider){
51281             provider = Roo.state.Manager;
51282         }
51283         var sm = new Roo.LayoutStateManager();
51284         sm.init(this, provider);
51285     },
51286
51287     /**
51288      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
51289      * object should contain properties for each region to add ContentPanels to, and each property's value should be
51290      * a valid ContentPanel config object.  Example:
51291      * <pre><code>
51292 // Create the main layout
51293 var layout = new Roo.BorderLayout('main-ct', {
51294     west: {
51295         split:true,
51296         minSize: 175,
51297         titlebar: true
51298     },
51299     center: {
51300         title:'Components'
51301     }
51302 }, 'main-ct');
51303
51304 // Create and add multiple ContentPanels at once via configs
51305 layout.batchAdd({
51306    west: {
51307        id: 'source-files',
51308        autoCreate:true,
51309        title:'Ext Source Files',
51310        autoScroll:true,
51311        fitToFrame:true
51312    },
51313    center : {
51314        el: cview,
51315        autoScroll:true,
51316        fitToFrame:true,
51317        toolbar: tb,
51318        resizeEl:'cbody'
51319    }
51320 });
51321 </code></pre>
51322      * @param {Object} regions An object containing ContentPanel configs by region name
51323      */
51324     batchAdd : function(regions){
51325         this.beginUpdate();
51326         for(var rname in regions){
51327             var lr = this.regions[rname];
51328             if(lr){
51329                 this.addTypedPanels(lr, regions[rname]);
51330             }
51331         }
51332         this.endUpdate();
51333     },
51334
51335     // private
51336     addTypedPanels : function(lr, ps){
51337         if(typeof ps == 'string'){
51338             lr.add(new Roo.ContentPanel(ps));
51339         }
51340         else if(ps instanceof Array){
51341             for(var i =0, len = ps.length; i < len; i++){
51342                 this.addTypedPanels(lr, ps[i]);
51343             }
51344         }
51345         else if(!ps.events){ // raw config?
51346             var el = ps.el;
51347             delete ps.el; // prevent conflict
51348             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
51349         }
51350         else {  // panel object assumed!
51351             lr.add(ps);
51352         }
51353     },
51354     /**
51355      * Adds a xtype elements to the layout.
51356      * <pre><code>
51357
51358 layout.addxtype({
51359        xtype : 'ContentPanel',
51360        region: 'west',
51361        items: [ .... ]
51362    }
51363 );
51364
51365 layout.addxtype({
51366         xtype : 'NestedLayoutPanel',
51367         region: 'west',
51368         layout: {
51369            center: { },
51370            west: { }   
51371         },
51372         items : [ ... list of content panels or nested layout panels.. ]
51373    }
51374 );
51375 </code></pre>
51376      * @param {Object} cfg Xtype definition of item to add.
51377      */
51378     addxtype : function(cfg)
51379     {
51380         // basically accepts a pannel...
51381         // can accept a layout region..!?!?
51382         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
51383         
51384         if (!cfg.xtype.match(/Panel$/)) {
51385             return false;
51386         }
51387         var ret = false;
51388         
51389         if (typeof(cfg.region) == 'undefined') {
51390             Roo.log("Failed to add Panel, region was not set");
51391             Roo.log(cfg);
51392             return false;
51393         }
51394         var region = cfg.region;
51395         delete cfg.region;
51396         
51397           
51398         var xitems = [];
51399         if (cfg.items) {
51400             xitems = cfg.items;
51401             delete cfg.items;
51402         }
51403         var nb = false;
51404         
51405         switch(cfg.xtype) 
51406         {
51407             case 'ContentPanel':  // ContentPanel (el, cfg)
51408             case 'ScrollPanel':  // ContentPanel (el, cfg)
51409             case 'ViewPanel': 
51410                 if(cfg.autoCreate) {
51411                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51412                 } else {
51413                     var el = this.el.createChild();
51414                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
51415                 }
51416                 
51417                 this.add(region, ret);
51418                 break;
51419             
51420             
51421             case 'TreePanel': // our new panel!
51422                 cfg.el = this.el.createChild();
51423                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51424                 this.add(region, ret);
51425                 break;
51426             
51427             case 'NestedLayoutPanel': 
51428                 // create a new Layout (which is  a Border Layout...
51429                 var el = this.el.createChild();
51430                 var clayout = cfg.layout;
51431                 delete cfg.layout;
51432                 clayout.items   = clayout.items  || [];
51433                 // replace this exitems with the clayout ones..
51434                 xitems = clayout.items;
51435                  
51436                 
51437                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
51438                     cfg.background = false;
51439                 }
51440                 var layout = new Roo.BorderLayout(el, clayout);
51441                 
51442                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
51443                 //console.log('adding nested layout panel '  + cfg.toSource());
51444                 this.add(region, ret);
51445                 nb = {}; /// find first...
51446                 break;
51447                 
51448             case 'GridPanel': 
51449             
51450                 // needs grid and region
51451                 
51452                 //var el = this.getRegion(region).el.createChild();
51453                 var el = this.el.createChild();
51454                 // create the grid first...
51455                 
51456                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
51457                 delete cfg.grid;
51458                 if (region == 'center' && this.active ) {
51459                     cfg.background = false;
51460                 }
51461                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
51462                 
51463                 this.add(region, ret);
51464                 if (cfg.background) {
51465                     ret.on('activate', function(gp) {
51466                         if (!gp.grid.rendered) {
51467                             gp.grid.render();
51468                         }
51469                     });
51470                 } else {
51471                     grid.render();
51472                 }
51473                 break;
51474            
51475            
51476            
51477                 
51478                 
51479                 
51480             default:
51481                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
51482                     
51483                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51484                     this.add(region, ret);
51485                 } else {
51486                 
51487                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
51488                     return null;
51489                 }
51490                 
51491              // GridPanel (grid, cfg)
51492             
51493         }
51494         this.beginUpdate();
51495         // add children..
51496         var region = '';
51497         var abn = {};
51498         Roo.each(xitems, function(i)  {
51499             region = nb && i.region ? i.region : false;
51500             
51501             var add = ret.addxtype(i);
51502            
51503             if (region) {
51504                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
51505                 if (!i.background) {
51506                     abn[region] = nb[region] ;
51507                 }
51508             }
51509             
51510         });
51511         this.endUpdate();
51512
51513         // make the last non-background panel active..
51514         //if (nb) { Roo.log(abn); }
51515         if (nb) {
51516             
51517             for(var r in abn) {
51518                 region = this.getRegion(r);
51519                 if (region) {
51520                     // tried using nb[r], but it does not work..
51521                      
51522                     region.showPanel(abn[r]);
51523                    
51524                 }
51525             }
51526         }
51527         return ret;
51528         
51529     }
51530 });
51531
51532 /**
51533  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
51534  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
51535  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
51536  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
51537  * <pre><code>
51538 // shorthand
51539 var CP = Roo.ContentPanel;
51540
51541 var layout = Roo.BorderLayout.create({
51542     north: {
51543         initialSize: 25,
51544         titlebar: false,
51545         panels: [new CP("north", "North")]
51546     },
51547     west: {
51548         split:true,
51549         initialSize: 200,
51550         minSize: 175,
51551         maxSize: 400,
51552         titlebar: true,
51553         collapsible: true,
51554         panels: [new CP("west", {title: "West"})]
51555     },
51556     east: {
51557         split:true,
51558         initialSize: 202,
51559         minSize: 175,
51560         maxSize: 400,
51561         titlebar: true,
51562         collapsible: true,
51563         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
51564     },
51565     south: {
51566         split:true,
51567         initialSize: 100,
51568         minSize: 100,
51569         maxSize: 200,
51570         titlebar: true,
51571         collapsible: true,
51572         panels: [new CP("south", {title: "South", closable: true})]
51573     },
51574     center: {
51575         titlebar: true,
51576         autoScroll:true,
51577         resizeTabs: true,
51578         minTabWidth: 50,
51579         preferredTabWidth: 150,
51580         panels: [
51581             new CP("center1", {title: "Close Me", closable: true}),
51582             new CP("center2", {title: "Center Panel", closable: false})
51583         ]
51584     }
51585 }, document.body);
51586
51587 layout.getRegion("center").showPanel("center1");
51588 </code></pre>
51589  * @param config
51590  * @param targetEl
51591  */
51592 Roo.BorderLayout.create = function(config, targetEl){
51593     var layout = new Roo.BorderLayout(targetEl || document.body, config);
51594     layout.beginUpdate();
51595     var regions = Roo.BorderLayout.RegionFactory.validRegions;
51596     for(var j = 0, jlen = regions.length; j < jlen; j++){
51597         var lr = regions[j];
51598         if(layout.regions[lr] && config[lr].panels){
51599             var r = layout.regions[lr];
51600             var ps = config[lr].panels;
51601             layout.addTypedPanels(r, ps);
51602         }
51603     }
51604     layout.endUpdate();
51605     return layout;
51606 };
51607
51608 // private
51609 Roo.BorderLayout.RegionFactory = {
51610     // private
51611     validRegions : ["north","south","east","west","center"],
51612
51613     // private
51614     create : function(target, mgr, config){
51615         target = target.toLowerCase();
51616         if(config.lightweight || config.basic){
51617             return new Roo.BasicLayoutRegion(mgr, config, target);
51618         }
51619         switch(target){
51620             case "north":
51621                 return new Roo.NorthLayoutRegion(mgr, config);
51622             case "south":
51623                 return new Roo.SouthLayoutRegion(mgr, config);
51624             case "east":
51625                 return new Roo.EastLayoutRegion(mgr, config);
51626             case "west":
51627                 return new Roo.WestLayoutRegion(mgr, config);
51628             case "center":
51629                 return new Roo.CenterLayoutRegion(mgr, config);
51630         }
51631         throw 'Layout region "'+target+'" not supported.';
51632     }
51633 };/*
51634  * Based on:
51635  * Ext JS Library 1.1.1
51636  * Copyright(c) 2006-2007, Ext JS, LLC.
51637  *
51638  * Originally Released Under LGPL - original licence link has changed is not relivant.
51639  *
51640  * Fork - LGPL
51641  * <script type="text/javascript">
51642  */
51643  
51644 /**
51645  * @class Roo.BasicLayoutRegion
51646  * @extends Roo.util.Observable
51647  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
51648  * and does not have a titlebar, tabs or any other features. All it does is size and position 
51649  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
51650  */
51651 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
51652     this.mgr = mgr;
51653     this.position  = pos;
51654     this.events = {
51655         /**
51656          * @scope Roo.BasicLayoutRegion
51657          */
51658         
51659         /**
51660          * @event beforeremove
51661          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
51662          * @param {Roo.LayoutRegion} this
51663          * @param {Roo.ContentPanel} panel The panel
51664          * @param {Object} e The cancel event object
51665          */
51666         "beforeremove" : true,
51667         /**
51668          * @event invalidated
51669          * Fires when the layout for this region is changed.
51670          * @param {Roo.LayoutRegion} this
51671          */
51672         "invalidated" : true,
51673         /**
51674          * @event visibilitychange
51675          * Fires when this region is shown or hidden 
51676          * @param {Roo.LayoutRegion} this
51677          * @param {Boolean} visibility true or false
51678          */
51679         "visibilitychange" : true,
51680         /**
51681          * @event paneladded
51682          * Fires when a panel is added. 
51683          * @param {Roo.LayoutRegion} this
51684          * @param {Roo.ContentPanel} panel The panel
51685          */
51686         "paneladded" : true,
51687         /**
51688          * @event panelremoved
51689          * Fires when a panel is removed. 
51690          * @param {Roo.LayoutRegion} this
51691          * @param {Roo.ContentPanel} panel The panel
51692          */
51693         "panelremoved" : true,
51694         /**
51695          * @event beforecollapse
51696          * Fires when this region before collapse.
51697          * @param {Roo.LayoutRegion} this
51698          */
51699         "beforecollapse" : true,
51700         /**
51701          * @event collapsed
51702          * Fires when this region is collapsed.
51703          * @param {Roo.LayoutRegion} this
51704          */
51705         "collapsed" : true,
51706         /**
51707          * @event expanded
51708          * Fires when this region is expanded.
51709          * @param {Roo.LayoutRegion} this
51710          */
51711         "expanded" : true,
51712         /**
51713          * @event slideshow
51714          * Fires when this region is slid into view.
51715          * @param {Roo.LayoutRegion} this
51716          */
51717         "slideshow" : true,
51718         /**
51719          * @event slidehide
51720          * Fires when this region slides out of view. 
51721          * @param {Roo.LayoutRegion} this
51722          */
51723         "slidehide" : true,
51724         /**
51725          * @event panelactivated
51726          * Fires when a panel is activated. 
51727          * @param {Roo.LayoutRegion} this
51728          * @param {Roo.ContentPanel} panel The activated panel
51729          */
51730         "panelactivated" : true,
51731         /**
51732          * @event resized
51733          * Fires when the user resizes this region. 
51734          * @param {Roo.LayoutRegion} this
51735          * @param {Number} newSize The new size (width for east/west, height for north/south)
51736          */
51737         "resized" : true
51738     };
51739     /** A collection of panels in this region. @type Roo.util.MixedCollection */
51740     this.panels = new Roo.util.MixedCollection();
51741     this.panels.getKey = this.getPanelId.createDelegate(this);
51742     this.box = null;
51743     this.activePanel = null;
51744     // ensure listeners are added...
51745     
51746     if (config.listeners || config.events) {
51747         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
51748             listeners : config.listeners || {},
51749             events : config.events || {}
51750         });
51751     }
51752     
51753     if(skipConfig !== true){
51754         this.applyConfig(config);
51755     }
51756 };
51757
51758 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
51759     getPanelId : function(p){
51760         return p.getId();
51761     },
51762     
51763     applyConfig : function(config){
51764         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51765         this.config = config;
51766         
51767     },
51768     
51769     /**
51770      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
51771      * the width, for horizontal (north, south) the height.
51772      * @param {Number} newSize The new width or height
51773      */
51774     resizeTo : function(newSize){
51775         var el = this.el ? this.el :
51776                  (this.activePanel ? this.activePanel.getEl() : null);
51777         if(el){
51778             switch(this.position){
51779                 case "east":
51780                 case "west":
51781                     el.setWidth(newSize);
51782                     this.fireEvent("resized", this, newSize);
51783                 break;
51784                 case "north":
51785                 case "south":
51786                     el.setHeight(newSize);
51787                     this.fireEvent("resized", this, newSize);
51788                 break;                
51789             }
51790         }
51791     },
51792     
51793     getBox : function(){
51794         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
51795     },
51796     
51797     getMargins : function(){
51798         return this.margins;
51799     },
51800     
51801     updateBox : function(box){
51802         this.box = box;
51803         var el = this.activePanel.getEl();
51804         el.dom.style.left = box.x + "px";
51805         el.dom.style.top = box.y + "px";
51806         this.activePanel.setSize(box.width, box.height);
51807     },
51808     
51809     /**
51810      * Returns the container element for this region.
51811      * @return {Roo.Element}
51812      */
51813     getEl : function(){
51814         return this.activePanel;
51815     },
51816     
51817     /**
51818      * Returns true if this region is currently visible.
51819      * @return {Boolean}
51820      */
51821     isVisible : function(){
51822         return this.activePanel ? true : false;
51823     },
51824     
51825     setActivePanel : function(panel){
51826         panel = this.getPanel(panel);
51827         if(this.activePanel && this.activePanel != panel){
51828             this.activePanel.setActiveState(false);
51829             this.activePanel.getEl().setLeftTop(-10000,-10000);
51830         }
51831         this.activePanel = panel;
51832         panel.setActiveState(true);
51833         if(this.box){
51834             panel.setSize(this.box.width, this.box.height);
51835         }
51836         this.fireEvent("panelactivated", this, panel);
51837         this.fireEvent("invalidated");
51838     },
51839     
51840     /**
51841      * Show the specified panel.
51842      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
51843      * @return {Roo.ContentPanel} The shown panel or null
51844      */
51845     showPanel : function(panel){
51846         if(panel = this.getPanel(panel)){
51847             this.setActivePanel(panel);
51848         }
51849         return panel;
51850     },
51851     
51852     /**
51853      * Get the active panel for this region.
51854      * @return {Roo.ContentPanel} The active panel or null
51855      */
51856     getActivePanel : function(){
51857         return this.activePanel;
51858     },
51859     
51860     /**
51861      * Add the passed ContentPanel(s)
51862      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
51863      * @return {Roo.ContentPanel} The panel added (if only one was added)
51864      */
51865     add : function(panel){
51866         if(arguments.length > 1){
51867             for(var i = 0, len = arguments.length; i < len; i++) {
51868                 this.add(arguments[i]);
51869             }
51870             return null;
51871         }
51872         if(this.hasPanel(panel)){
51873             this.showPanel(panel);
51874             return panel;
51875         }
51876         var el = panel.getEl();
51877         if(el.dom.parentNode != this.mgr.el.dom){
51878             this.mgr.el.dom.appendChild(el.dom);
51879         }
51880         if(panel.setRegion){
51881             panel.setRegion(this);
51882         }
51883         this.panels.add(panel);
51884         el.setStyle("position", "absolute");
51885         if(!panel.background){
51886             this.setActivePanel(panel);
51887             if(this.config.initialSize && this.panels.getCount()==1){
51888                 this.resizeTo(this.config.initialSize);
51889             }
51890         }
51891         this.fireEvent("paneladded", this, panel);
51892         return panel;
51893     },
51894     
51895     /**
51896      * Returns true if the panel is in this region.
51897      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51898      * @return {Boolean}
51899      */
51900     hasPanel : function(panel){
51901         if(typeof panel == "object"){ // must be panel obj
51902             panel = panel.getId();
51903         }
51904         return this.getPanel(panel) ? true : false;
51905     },
51906     
51907     /**
51908      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
51909      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51910      * @param {Boolean} preservePanel Overrides the config preservePanel option
51911      * @return {Roo.ContentPanel} The panel that was removed
51912      */
51913     remove : function(panel, preservePanel){
51914         panel = this.getPanel(panel);
51915         if(!panel){
51916             return null;
51917         }
51918         var e = {};
51919         this.fireEvent("beforeremove", this, panel, e);
51920         if(e.cancel === true){
51921             return null;
51922         }
51923         var panelId = panel.getId();
51924         this.panels.removeKey(panelId);
51925         return panel;
51926     },
51927     
51928     /**
51929      * Returns the panel specified or null if it's not in this region.
51930      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51931      * @return {Roo.ContentPanel}
51932      */
51933     getPanel : function(id){
51934         if(typeof id == "object"){ // must be panel obj
51935             return id;
51936         }
51937         return this.panels.get(id);
51938     },
51939     
51940     /**
51941      * Returns this regions position (north/south/east/west/center).
51942      * @return {String} 
51943      */
51944     getPosition: function(){
51945         return this.position;    
51946     }
51947 });/*
51948  * Based on:
51949  * Ext JS Library 1.1.1
51950  * Copyright(c) 2006-2007, Ext JS, LLC.
51951  *
51952  * Originally Released Under LGPL - original licence link has changed is not relivant.
51953  *
51954  * Fork - LGPL
51955  * <script type="text/javascript">
51956  */
51957  
51958 /**
51959  * @class Roo.LayoutRegion
51960  * @extends Roo.BasicLayoutRegion
51961  * This class represents a region in a layout manager.
51962  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
51963  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
51964  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
51965  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
51966  * @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})
51967  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
51968  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
51969  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
51970  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
51971  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
51972  * @cfg {String}    title           The title for the region (overrides panel titles)
51973  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
51974  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
51975  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
51976  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
51977  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
51978  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
51979  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
51980  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
51981  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
51982  * @cfg {Boolean}   showPin         True to show a pin button
51983  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
51984  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
51985  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
51986  * @cfg {Number}    width           For East/West panels
51987  * @cfg {Number}    height          For North/South panels
51988  * @cfg {Boolean}   split           To show the splitter
51989  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
51990  */
51991 Roo.LayoutRegion = function(mgr, config, pos){
51992     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
51993     var dh = Roo.DomHelper;
51994     /** This region's container element 
51995     * @type Roo.Element */
51996     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
51997     /** This region's title element 
51998     * @type Roo.Element */
51999
52000     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
52001         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
52002         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
52003     ]}, true);
52004     this.titleEl.enableDisplayMode();
52005     /** This region's title text element 
52006     * @type HTMLElement */
52007     this.titleTextEl = this.titleEl.dom.firstChild;
52008     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
52009     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
52010     this.closeBtn.enableDisplayMode();
52011     this.closeBtn.on("click", this.closeClicked, this);
52012     this.closeBtn.hide();
52013
52014     this.createBody(config);
52015     this.visible = true;
52016     this.collapsed = false;
52017
52018     if(config.hideWhenEmpty){
52019         this.hide();
52020         this.on("paneladded", this.validateVisibility, this);
52021         this.on("panelremoved", this.validateVisibility, this);
52022     }
52023     this.applyConfig(config);
52024 };
52025
52026 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
52027
52028     createBody : function(){
52029         /** This region's body element 
52030         * @type Roo.Element */
52031         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
52032     },
52033
52034     applyConfig : function(c){
52035         if(c.collapsible && this.position != "center" && !this.collapsedEl){
52036             var dh = Roo.DomHelper;
52037             if(c.titlebar !== false){
52038                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
52039                 this.collapseBtn.on("click", this.collapse, this);
52040                 this.collapseBtn.enableDisplayMode();
52041
52042                 if(c.showPin === true || this.showPin){
52043                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
52044                     this.stickBtn.enableDisplayMode();
52045                     this.stickBtn.on("click", this.expand, this);
52046                     this.stickBtn.hide();
52047                 }
52048             }
52049             /** This region's collapsed element
52050             * @type Roo.Element */
52051             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
52052                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
52053             ]}, true);
52054             if(c.floatable !== false){
52055                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
52056                this.collapsedEl.on("click", this.collapseClick, this);
52057             }
52058
52059             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
52060                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
52061                    id: "message", unselectable: "on", style:{"float":"left"}});
52062                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
52063              }
52064             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
52065             this.expandBtn.on("click", this.expand, this);
52066         }
52067         if(this.collapseBtn){
52068             this.collapseBtn.setVisible(c.collapsible == true);
52069         }
52070         this.cmargins = c.cmargins || this.cmargins ||
52071                          (this.position == "west" || this.position == "east" ?
52072                              {top: 0, left: 2, right:2, bottom: 0} :
52073                              {top: 2, left: 0, right:0, bottom: 2});
52074         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52075         this.bottomTabs = c.tabPosition != "top";
52076         this.autoScroll = c.autoScroll || false;
52077         if(this.autoScroll){
52078             this.bodyEl.setStyle("overflow", "auto");
52079         }else{
52080             this.bodyEl.setStyle("overflow", "hidden");
52081         }
52082         //if(c.titlebar !== false){
52083             if((!c.titlebar && !c.title) || c.titlebar === false){
52084                 this.titleEl.hide();
52085             }else{
52086                 this.titleEl.show();
52087                 if(c.title){
52088                     this.titleTextEl.innerHTML = c.title;
52089                 }
52090             }
52091         //}
52092         this.duration = c.duration || .30;
52093         this.slideDuration = c.slideDuration || .45;
52094         this.config = c;
52095         if(c.collapsed){
52096             this.collapse(true);
52097         }
52098         if(c.hidden){
52099             this.hide();
52100         }
52101     },
52102     /**
52103      * Returns true if this region is currently visible.
52104      * @return {Boolean}
52105      */
52106     isVisible : function(){
52107         return this.visible;
52108     },
52109
52110     /**
52111      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
52112      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
52113      */
52114     setCollapsedTitle : function(title){
52115         title = title || "&#160;";
52116         if(this.collapsedTitleTextEl){
52117             this.collapsedTitleTextEl.innerHTML = title;
52118         }
52119     },
52120
52121     getBox : function(){
52122         var b;
52123         if(!this.collapsed){
52124             b = this.el.getBox(false, true);
52125         }else{
52126             b = this.collapsedEl.getBox(false, true);
52127         }
52128         return b;
52129     },
52130
52131     getMargins : function(){
52132         return this.collapsed ? this.cmargins : this.margins;
52133     },
52134
52135     highlight : function(){
52136         this.el.addClass("x-layout-panel-dragover");
52137     },
52138
52139     unhighlight : function(){
52140         this.el.removeClass("x-layout-panel-dragover");
52141     },
52142
52143     updateBox : function(box){
52144         this.box = box;
52145         if(!this.collapsed){
52146             this.el.dom.style.left = box.x + "px";
52147             this.el.dom.style.top = box.y + "px";
52148             this.updateBody(box.width, box.height);
52149         }else{
52150             this.collapsedEl.dom.style.left = box.x + "px";
52151             this.collapsedEl.dom.style.top = box.y + "px";
52152             this.collapsedEl.setSize(box.width, box.height);
52153         }
52154         if(this.tabs){
52155             this.tabs.autoSizeTabs();
52156         }
52157     },
52158
52159     updateBody : function(w, h){
52160         if(w !== null){
52161             this.el.setWidth(w);
52162             w -= this.el.getBorderWidth("rl");
52163             if(this.config.adjustments){
52164                 w += this.config.adjustments[0];
52165             }
52166         }
52167         if(h !== null){
52168             this.el.setHeight(h);
52169             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
52170             h -= this.el.getBorderWidth("tb");
52171             if(this.config.adjustments){
52172                 h += this.config.adjustments[1];
52173             }
52174             this.bodyEl.setHeight(h);
52175             if(this.tabs){
52176                 h = this.tabs.syncHeight(h);
52177             }
52178         }
52179         if(this.panelSize){
52180             w = w !== null ? w : this.panelSize.width;
52181             h = h !== null ? h : this.panelSize.height;
52182         }
52183         if(this.activePanel){
52184             var el = this.activePanel.getEl();
52185             w = w !== null ? w : el.getWidth();
52186             h = h !== null ? h : el.getHeight();
52187             this.panelSize = {width: w, height: h};
52188             this.activePanel.setSize(w, h);
52189         }
52190         if(Roo.isIE && this.tabs){
52191             this.tabs.el.repaint();
52192         }
52193     },
52194
52195     /**
52196      * Returns the container element for this region.
52197      * @return {Roo.Element}
52198      */
52199     getEl : function(){
52200         return this.el;
52201     },
52202
52203     /**
52204      * Hides this region.
52205      */
52206     hide : function(){
52207         if(!this.collapsed){
52208             this.el.dom.style.left = "-2000px";
52209             this.el.hide();
52210         }else{
52211             this.collapsedEl.dom.style.left = "-2000px";
52212             this.collapsedEl.hide();
52213         }
52214         this.visible = false;
52215         this.fireEvent("visibilitychange", this, false);
52216     },
52217
52218     /**
52219      * Shows this region if it was previously hidden.
52220      */
52221     show : function(){
52222         if(!this.collapsed){
52223             this.el.show();
52224         }else{
52225             this.collapsedEl.show();
52226         }
52227         this.visible = true;
52228         this.fireEvent("visibilitychange", this, true);
52229     },
52230
52231     closeClicked : function(){
52232         if(this.activePanel){
52233             this.remove(this.activePanel);
52234         }
52235     },
52236
52237     collapseClick : function(e){
52238         if(this.isSlid){
52239            e.stopPropagation();
52240            this.slideIn();
52241         }else{
52242            e.stopPropagation();
52243            this.slideOut();
52244         }
52245     },
52246
52247     /**
52248      * Collapses this region.
52249      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
52250      */
52251     collapse : function(skipAnim, skipCheck = false){
52252         if(this.collapsed) {
52253             return;
52254         }
52255         
52256         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
52257             
52258             this.collapsed = true;
52259             if(this.split){
52260                 this.split.el.hide();
52261             }
52262             if(this.config.animate && skipAnim !== true){
52263                 this.fireEvent("invalidated", this);
52264                 this.animateCollapse();
52265             }else{
52266                 this.el.setLocation(-20000,-20000);
52267                 this.el.hide();
52268                 this.collapsedEl.show();
52269                 this.fireEvent("collapsed", this);
52270                 this.fireEvent("invalidated", this);
52271             }
52272         }
52273         
52274     },
52275
52276     animateCollapse : function(){
52277         // overridden
52278     },
52279
52280     /**
52281      * Expands this region if it was previously collapsed.
52282      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
52283      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
52284      */
52285     expand : function(e, skipAnim){
52286         if(e) {
52287             e.stopPropagation();
52288         }
52289         if(!this.collapsed || this.el.hasActiveFx()) {
52290             return;
52291         }
52292         if(this.isSlid){
52293             this.afterSlideIn();
52294             skipAnim = true;
52295         }
52296         this.collapsed = false;
52297         if(this.config.animate && skipAnim !== true){
52298             this.animateExpand();
52299         }else{
52300             this.el.show();
52301             if(this.split){
52302                 this.split.el.show();
52303             }
52304             this.collapsedEl.setLocation(-2000,-2000);
52305             this.collapsedEl.hide();
52306             this.fireEvent("invalidated", this);
52307             this.fireEvent("expanded", this);
52308         }
52309     },
52310
52311     animateExpand : function(){
52312         // overridden
52313     },
52314
52315     initTabs : function()
52316     {
52317         this.bodyEl.setStyle("overflow", "hidden");
52318         var ts = new Roo.TabPanel(
52319                 this.bodyEl.dom,
52320                 {
52321                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
52322                     disableTooltips: this.config.disableTabTips,
52323                     toolbar : this.config.toolbar
52324                 }
52325         );
52326         if(this.config.hideTabs){
52327             ts.stripWrap.setDisplayed(false);
52328         }
52329         this.tabs = ts;
52330         ts.resizeTabs = this.config.resizeTabs === true;
52331         ts.minTabWidth = this.config.minTabWidth || 40;
52332         ts.maxTabWidth = this.config.maxTabWidth || 250;
52333         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
52334         ts.monitorResize = false;
52335         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52336         ts.bodyEl.addClass('x-layout-tabs-body');
52337         this.panels.each(this.initPanelAsTab, this);
52338     },
52339
52340     initPanelAsTab : function(panel){
52341         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
52342                     this.config.closeOnTab && panel.isClosable());
52343         if(panel.tabTip !== undefined){
52344             ti.setTooltip(panel.tabTip);
52345         }
52346         ti.on("activate", function(){
52347               this.setActivePanel(panel);
52348         }, this);
52349         if(this.config.closeOnTab){
52350             ti.on("beforeclose", function(t, e){
52351                 e.cancel = true;
52352                 this.remove(panel);
52353             }, this);
52354         }
52355         return ti;
52356     },
52357
52358     updatePanelTitle : function(panel, title){
52359         if(this.activePanel == panel){
52360             this.updateTitle(title);
52361         }
52362         if(this.tabs){
52363             var ti = this.tabs.getTab(panel.getEl().id);
52364             ti.setText(title);
52365             if(panel.tabTip !== undefined){
52366                 ti.setTooltip(panel.tabTip);
52367             }
52368         }
52369     },
52370
52371     updateTitle : function(title){
52372         if(this.titleTextEl && !this.config.title){
52373             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
52374         }
52375     },
52376
52377     setActivePanel : function(panel){
52378         panel = this.getPanel(panel);
52379         if(this.activePanel && this.activePanel != panel){
52380             this.activePanel.setActiveState(false);
52381         }
52382         this.activePanel = panel;
52383         panel.setActiveState(true);
52384         if(this.panelSize){
52385             panel.setSize(this.panelSize.width, this.panelSize.height);
52386         }
52387         if(this.closeBtn){
52388             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
52389         }
52390         this.updateTitle(panel.getTitle());
52391         if(this.tabs){
52392             this.fireEvent("invalidated", this);
52393         }
52394         this.fireEvent("panelactivated", this, panel);
52395     },
52396
52397     /**
52398      * Shows the specified panel.
52399      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
52400      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
52401      */
52402     showPanel : function(panel)
52403     {
52404         panel = this.getPanel(panel);
52405         if(panel){
52406             if(this.tabs){
52407                 var tab = this.tabs.getTab(panel.getEl().id);
52408                 if(tab.isHidden()){
52409                     this.tabs.unhideTab(tab.id);
52410                 }
52411                 tab.activate();
52412             }else{
52413                 this.setActivePanel(panel);
52414             }
52415         }
52416         return panel;
52417     },
52418
52419     /**
52420      * Get the active panel for this region.
52421      * @return {Roo.ContentPanel} The active panel or null
52422      */
52423     getActivePanel : function(){
52424         return this.activePanel;
52425     },
52426
52427     validateVisibility : function(){
52428         if(this.panels.getCount() < 1){
52429             this.updateTitle("&#160;");
52430             this.closeBtn.hide();
52431             this.hide();
52432         }else{
52433             if(!this.isVisible()){
52434                 this.show();
52435             }
52436         }
52437     },
52438
52439     /**
52440      * Adds the passed ContentPanel(s) to this region.
52441      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52442      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
52443      */
52444     add : function(panel){
52445         if(arguments.length > 1){
52446             for(var i = 0, len = arguments.length; i < len; i++) {
52447                 this.add(arguments[i]);
52448             }
52449             return null;
52450         }
52451         if(this.hasPanel(panel)){
52452             this.showPanel(panel);
52453             return panel;
52454         }
52455         panel.setRegion(this);
52456         this.panels.add(panel);
52457         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
52458             this.bodyEl.dom.appendChild(panel.getEl().dom);
52459             if(panel.background !== true){
52460                 this.setActivePanel(panel);
52461             }
52462             this.fireEvent("paneladded", this, panel);
52463             return panel;
52464         }
52465         if(!this.tabs){
52466             this.initTabs();
52467         }else{
52468             this.initPanelAsTab(panel);
52469         }
52470         if(panel.background !== true){
52471             this.tabs.activate(panel.getEl().id);
52472         }
52473         this.fireEvent("paneladded", this, panel);
52474         return panel;
52475     },
52476
52477     /**
52478      * Hides the tab for the specified panel.
52479      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52480      */
52481     hidePanel : function(panel){
52482         if(this.tabs && (panel = this.getPanel(panel))){
52483             this.tabs.hideTab(panel.getEl().id);
52484         }
52485     },
52486
52487     /**
52488      * Unhides the tab for a previously hidden panel.
52489      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52490      */
52491     unhidePanel : function(panel){
52492         if(this.tabs && (panel = this.getPanel(panel))){
52493             this.tabs.unhideTab(panel.getEl().id);
52494         }
52495     },
52496
52497     clearPanels : function(){
52498         while(this.panels.getCount() > 0){
52499              this.remove(this.panels.first());
52500         }
52501     },
52502
52503     /**
52504      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52505      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52506      * @param {Boolean} preservePanel Overrides the config preservePanel option
52507      * @return {Roo.ContentPanel} The panel that was removed
52508      */
52509     remove : function(panel, preservePanel){
52510         panel = this.getPanel(panel);
52511         if(!panel){
52512             return null;
52513         }
52514         var e = {};
52515         this.fireEvent("beforeremove", this, panel, e);
52516         if(e.cancel === true){
52517             return null;
52518         }
52519         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
52520         var panelId = panel.getId();
52521         this.panels.removeKey(panelId);
52522         if(preservePanel){
52523             document.body.appendChild(panel.getEl().dom);
52524         }
52525         if(this.tabs){
52526             this.tabs.removeTab(panel.getEl().id);
52527         }else if (!preservePanel){
52528             this.bodyEl.dom.removeChild(panel.getEl().dom);
52529         }
52530         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
52531             var p = this.panels.first();
52532             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
52533             tempEl.appendChild(p.getEl().dom);
52534             this.bodyEl.update("");
52535             this.bodyEl.dom.appendChild(p.getEl().dom);
52536             tempEl = null;
52537             this.updateTitle(p.getTitle());
52538             this.tabs = null;
52539             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52540             this.setActivePanel(p);
52541         }
52542         panel.setRegion(null);
52543         if(this.activePanel == panel){
52544             this.activePanel = null;
52545         }
52546         if(this.config.autoDestroy !== false && preservePanel !== true){
52547             try{panel.destroy();}catch(e){}
52548         }
52549         this.fireEvent("panelremoved", this, panel);
52550         return panel;
52551     },
52552
52553     /**
52554      * Returns the TabPanel component used by this region
52555      * @return {Roo.TabPanel}
52556      */
52557     getTabs : function(){
52558         return this.tabs;
52559     },
52560
52561     createTool : function(parentEl, className){
52562         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
52563             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
52564         btn.addClassOnOver("x-layout-tools-button-over");
52565         return btn;
52566     }
52567 });/*
52568  * Based on:
52569  * Ext JS Library 1.1.1
52570  * Copyright(c) 2006-2007, Ext JS, LLC.
52571  *
52572  * Originally Released Under LGPL - original licence link has changed is not relivant.
52573  *
52574  * Fork - LGPL
52575  * <script type="text/javascript">
52576  */
52577  
52578
52579
52580 /**
52581  * @class Roo.SplitLayoutRegion
52582  * @extends Roo.LayoutRegion
52583  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
52584  */
52585 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
52586     this.cursor = cursor;
52587     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
52588 };
52589
52590 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
52591     splitTip : "Drag to resize.",
52592     collapsibleSplitTip : "Drag to resize. Double click to hide.",
52593     useSplitTips : false,
52594
52595     applyConfig : function(config){
52596         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
52597         if(config.split){
52598             if(!this.split){
52599                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
52600                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
52601                 /** The SplitBar for this region 
52602                 * @type Roo.SplitBar */
52603                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
52604                 this.split.on("moved", this.onSplitMove, this);
52605                 this.split.useShim = config.useShim === true;
52606                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
52607                 if(this.useSplitTips){
52608                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
52609                 }
52610                 if(config.collapsible){
52611                     this.split.el.on("dblclick", this.collapse,  this);
52612                 }
52613             }
52614             if(typeof config.minSize != "undefined"){
52615                 this.split.minSize = config.minSize;
52616             }
52617             if(typeof config.maxSize != "undefined"){
52618                 this.split.maxSize = config.maxSize;
52619             }
52620             if(config.hideWhenEmpty || config.hidden || config.collapsed){
52621                 this.hideSplitter();
52622             }
52623         }
52624     },
52625
52626     getHMaxSize : function(){
52627          var cmax = this.config.maxSize || 10000;
52628          var center = this.mgr.getRegion("center");
52629          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
52630     },
52631
52632     getVMaxSize : function(){
52633          var cmax = this.config.maxSize || 10000;
52634          var center = this.mgr.getRegion("center");
52635          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
52636     },
52637
52638     onSplitMove : function(split, newSize){
52639         this.fireEvent("resized", this, newSize);
52640     },
52641     
52642     /** 
52643      * Returns the {@link Roo.SplitBar} for this region.
52644      * @return {Roo.SplitBar}
52645      */
52646     getSplitBar : function(){
52647         return this.split;
52648     },
52649     
52650     hide : function(){
52651         this.hideSplitter();
52652         Roo.SplitLayoutRegion.superclass.hide.call(this);
52653     },
52654
52655     hideSplitter : function(){
52656         if(this.split){
52657             this.split.el.setLocation(-2000,-2000);
52658             this.split.el.hide();
52659         }
52660     },
52661
52662     show : function(){
52663         if(this.split){
52664             this.split.el.show();
52665         }
52666         Roo.SplitLayoutRegion.superclass.show.call(this);
52667     },
52668     
52669     beforeSlide: function(){
52670         if(Roo.isGecko){// firefox overflow auto bug workaround
52671             this.bodyEl.clip();
52672             if(this.tabs) {
52673                 this.tabs.bodyEl.clip();
52674             }
52675             if(this.activePanel){
52676                 this.activePanel.getEl().clip();
52677                 
52678                 if(this.activePanel.beforeSlide){
52679                     this.activePanel.beforeSlide();
52680                 }
52681             }
52682         }
52683     },
52684     
52685     afterSlide : function(){
52686         if(Roo.isGecko){// firefox overflow auto bug workaround
52687             this.bodyEl.unclip();
52688             if(this.tabs) {
52689                 this.tabs.bodyEl.unclip();
52690             }
52691             if(this.activePanel){
52692                 this.activePanel.getEl().unclip();
52693                 if(this.activePanel.afterSlide){
52694                     this.activePanel.afterSlide();
52695                 }
52696             }
52697         }
52698     },
52699
52700     initAutoHide : function(){
52701         if(this.autoHide !== false){
52702             if(!this.autoHideHd){
52703                 var st = new Roo.util.DelayedTask(this.slideIn, this);
52704                 this.autoHideHd = {
52705                     "mouseout": function(e){
52706                         if(!e.within(this.el, true)){
52707                             st.delay(500);
52708                         }
52709                     },
52710                     "mouseover" : function(e){
52711                         st.cancel();
52712                     },
52713                     scope : this
52714                 };
52715             }
52716             this.el.on(this.autoHideHd);
52717         }
52718     },
52719
52720     clearAutoHide : function(){
52721         if(this.autoHide !== false){
52722             this.el.un("mouseout", this.autoHideHd.mouseout);
52723             this.el.un("mouseover", this.autoHideHd.mouseover);
52724         }
52725     },
52726
52727     clearMonitor : function(){
52728         Roo.get(document).un("click", this.slideInIf, this);
52729     },
52730
52731     // these names are backwards but not changed for compat
52732     slideOut : function(){
52733         if(this.isSlid || this.el.hasActiveFx()){
52734             return;
52735         }
52736         this.isSlid = true;
52737         if(this.collapseBtn){
52738             this.collapseBtn.hide();
52739         }
52740         this.closeBtnState = this.closeBtn.getStyle('display');
52741         this.closeBtn.hide();
52742         if(this.stickBtn){
52743             this.stickBtn.show();
52744         }
52745         this.el.show();
52746         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
52747         this.beforeSlide();
52748         this.el.setStyle("z-index", 10001);
52749         this.el.slideIn(this.getSlideAnchor(), {
52750             callback: function(){
52751                 this.afterSlide();
52752                 this.initAutoHide();
52753                 Roo.get(document).on("click", this.slideInIf, this);
52754                 this.fireEvent("slideshow", this);
52755             },
52756             scope: this,
52757             block: true
52758         });
52759     },
52760
52761     afterSlideIn : function(){
52762         this.clearAutoHide();
52763         this.isSlid = false;
52764         this.clearMonitor();
52765         this.el.setStyle("z-index", "");
52766         if(this.collapseBtn){
52767             this.collapseBtn.show();
52768         }
52769         this.closeBtn.setStyle('display', this.closeBtnState);
52770         if(this.stickBtn){
52771             this.stickBtn.hide();
52772         }
52773         this.fireEvent("slidehide", this);
52774     },
52775
52776     slideIn : function(cb){
52777         if(!this.isSlid || this.el.hasActiveFx()){
52778             Roo.callback(cb);
52779             return;
52780         }
52781         this.isSlid = false;
52782         this.beforeSlide();
52783         this.el.slideOut(this.getSlideAnchor(), {
52784             callback: function(){
52785                 this.el.setLeftTop(-10000, -10000);
52786                 this.afterSlide();
52787                 this.afterSlideIn();
52788                 Roo.callback(cb);
52789             },
52790             scope: this,
52791             block: true
52792         });
52793     },
52794     
52795     slideInIf : function(e){
52796         if(!e.within(this.el)){
52797             this.slideIn();
52798         }
52799     },
52800
52801     animateCollapse : function(){
52802         this.beforeSlide();
52803         this.el.setStyle("z-index", 20000);
52804         var anchor = this.getSlideAnchor();
52805         this.el.slideOut(anchor, {
52806             callback : function(){
52807                 this.el.setStyle("z-index", "");
52808                 this.collapsedEl.slideIn(anchor, {duration:.3});
52809                 this.afterSlide();
52810                 this.el.setLocation(-10000,-10000);
52811                 this.el.hide();
52812                 this.fireEvent("collapsed", this);
52813             },
52814             scope: this,
52815             block: true
52816         });
52817     },
52818
52819     animateExpand : function(){
52820         this.beforeSlide();
52821         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
52822         this.el.setStyle("z-index", 20000);
52823         this.collapsedEl.hide({
52824             duration:.1
52825         });
52826         this.el.slideIn(this.getSlideAnchor(), {
52827             callback : function(){
52828                 this.el.setStyle("z-index", "");
52829                 this.afterSlide();
52830                 if(this.split){
52831                     this.split.el.show();
52832                 }
52833                 this.fireEvent("invalidated", this);
52834                 this.fireEvent("expanded", this);
52835             },
52836             scope: this,
52837             block: true
52838         });
52839     },
52840
52841     anchors : {
52842         "west" : "left",
52843         "east" : "right",
52844         "north" : "top",
52845         "south" : "bottom"
52846     },
52847
52848     sanchors : {
52849         "west" : "l",
52850         "east" : "r",
52851         "north" : "t",
52852         "south" : "b"
52853     },
52854
52855     canchors : {
52856         "west" : "tl-tr",
52857         "east" : "tr-tl",
52858         "north" : "tl-bl",
52859         "south" : "bl-tl"
52860     },
52861
52862     getAnchor : function(){
52863         return this.anchors[this.position];
52864     },
52865
52866     getCollapseAnchor : function(){
52867         return this.canchors[this.position];
52868     },
52869
52870     getSlideAnchor : function(){
52871         return this.sanchors[this.position];
52872     },
52873
52874     getAlignAdj : function(){
52875         var cm = this.cmargins;
52876         switch(this.position){
52877             case "west":
52878                 return [0, 0];
52879             break;
52880             case "east":
52881                 return [0, 0];
52882             break;
52883             case "north":
52884                 return [0, 0];
52885             break;
52886             case "south":
52887                 return [0, 0];
52888             break;
52889         }
52890     },
52891
52892     getExpandAdj : function(){
52893         var c = this.collapsedEl, cm = this.cmargins;
52894         switch(this.position){
52895             case "west":
52896                 return [-(cm.right+c.getWidth()+cm.left), 0];
52897             break;
52898             case "east":
52899                 return [cm.right+c.getWidth()+cm.left, 0];
52900             break;
52901             case "north":
52902                 return [0, -(cm.top+cm.bottom+c.getHeight())];
52903             break;
52904             case "south":
52905                 return [0, cm.top+cm.bottom+c.getHeight()];
52906             break;
52907         }
52908     }
52909 });/*
52910  * Based on:
52911  * Ext JS Library 1.1.1
52912  * Copyright(c) 2006-2007, Ext JS, LLC.
52913  *
52914  * Originally Released Under LGPL - original licence link has changed is not relivant.
52915  *
52916  * Fork - LGPL
52917  * <script type="text/javascript">
52918  */
52919 /*
52920  * These classes are private internal classes
52921  */
52922 Roo.CenterLayoutRegion = function(mgr, config){
52923     Roo.LayoutRegion.call(this, mgr, config, "center");
52924     this.visible = true;
52925     this.minWidth = config.minWidth || 20;
52926     this.minHeight = config.minHeight || 20;
52927 };
52928
52929 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
52930     hide : function(){
52931         // center panel can't be hidden
52932     },
52933     
52934     show : function(){
52935         // center panel can't be hidden
52936     },
52937     
52938     getMinWidth: function(){
52939         return this.minWidth;
52940     },
52941     
52942     getMinHeight: function(){
52943         return this.minHeight;
52944     }
52945 });
52946
52947
52948 Roo.NorthLayoutRegion = function(mgr, config){
52949     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
52950     if(this.split){
52951         this.split.placement = Roo.SplitBar.TOP;
52952         this.split.orientation = Roo.SplitBar.VERTICAL;
52953         this.split.el.addClass("x-layout-split-v");
52954     }
52955     var size = config.initialSize || config.height;
52956     if(typeof size != "undefined"){
52957         this.el.setHeight(size);
52958     }
52959 };
52960 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
52961     orientation: Roo.SplitBar.VERTICAL,
52962     getBox : function(){
52963         if(this.collapsed){
52964             return this.collapsedEl.getBox();
52965         }
52966         var box = this.el.getBox();
52967         if(this.split){
52968             box.height += this.split.el.getHeight();
52969         }
52970         return box;
52971     },
52972     
52973     updateBox : function(box){
52974         if(this.split && !this.collapsed){
52975             box.height -= this.split.el.getHeight();
52976             this.split.el.setLeft(box.x);
52977             this.split.el.setTop(box.y+box.height);
52978             this.split.el.setWidth(box.width);
52979         }
52980         if(this.collapsed){
52981             this.updateBody(box.width, null);
52982         }
52983         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52984     }
52985 });
52986
52987 Roo.SouthLayoutRegion = function(mgr, config){
52988     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
52989     if(this.split){
52990         this.split.placement = Roo.SplitBar.BOTTOM;
52991         this.split.orientation = Roo.SplitBar.VERTICAL;
52992         this.split.el.addClass("x-layout-split-v");
52993     }
52994     var size = config.initialSize || config.height;
52995     if(typeof size != "undefined"){
52996         this.el.setHeight(size);
52997     }
52998 };
52999 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
53000     orientation: Roo.SplitBar.VERTICAL,
53001     getBox : function(){
53002         if(this.collapsed){
53003             return this.collapsedEl.getBox();
53004         }
53005         var box = this.el.getBox();
53006         if(this.split){
53007             var sh = this.split.el.getHeight();
53008             box.height += sh;
53009             box.y -= sh;
53010         }
53011         return box;
53012     },
53013     
53014     updateBox : function(box){
53015         if(this.split && !this.collapsed){
53016             var sh = this.split.el.getHeight();
53017             box.height -= sh;
53018             box.y += sh;
53019             this.split.el.setLeft(box.x);
53020             this.split.el.setTop(box.y-sh);
53021             this.split.el.setWidth(box.width);
53022         }
53023         if(this.collapsed){
53024             this.updateBody(box.width, null);
53025         }
53026         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53027     }
53028 });
53029
53030 Roo.EastLayoutRegion = function(mgr, config){
53031     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
53032     if(this.split){
53033         this.split.placement = Roo.SplitBar.RIGHT;
53034         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53035         this.split.el.addClass("x-layout-split-h");
53036     }
53037     var size = config.initialSize || config.width;
53038     if(typeof size != "undefined"){
53039         this.el.setWidth(size);
53040     }
53041 };
53042 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
53043     orientation: Roo.SplitBar.HORIZONTAL,
53044     getBox : function(){
53045         if(this.collapsed){
53046             return this.collapsedEl.getBox();
53047         }
53048         var box = this.el.getBox();
53049         if(this.split){
53050             var sw = this.split.el.getWidth();
53051             box.width += sw;
53052             box.x -= sw;
53053         }
53054         return box;
53055     },
53056
53057     updateBox : function(box){
53058         if(this.split && !this.collapsed){
53059             var sw = this.split.el.getWidth();
53060             box.width -= sw;
53061             this.split.el.setLeft(box.x);
53062             this.split.el.setTop(box.y);
53063             this.split.el.setHeight(box.height);
53064             box.x += sw;
53065         }
53066         if(this.collapsed){
53067             this.updateBody(null, box.height);
53068         }
53069         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53070     }
53071 });
53072
53073 Roo.WestLayoutRegion = function(mgr, config){
53074     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
53075     if(this.split){
53076         this.split.placement = Roo.SplitBar.LEFT;
53077         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53078         this.split.el.addClass("x-layout-split-h");
53079     }
53080     var size = config.initialSize || config.width;
53081     if(typeof size != "undefined"){
53082         this.el.setWidth(size);
53083     }
53084 };
53085 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
53086     orientation: Roo.SplitBar.HORIZONTAL,
53087     getBox : function(){
53088         if(this.collapsed){
53089             return this.collapsedEl.getBox();
53090         }
53091         var box = this.el.getBox();
53092         if(this.split){
53093             box.width += this.split.el.getWidth();
53094         }
53095         return box;
53096     },
53097     
53098     updateBox : function(box){
53099         if(this.split && !this.collapsed){
53100             var sw = this.split.el.getWidth();
53101             box.width -= sw;
53102             this.split.el.setLeft(box.x+box.width);
53103             this.split.el.setTop(box.y);
53104             this.split.el.setHeight(box.height);
53105         }
53106         if(this.collapsed){
53107             this.updateBody(null, box.height);
53108         }
53109         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53110     }
53111 });
53112 /*
53113  * Based on:
53114  * Ext JS Library 1.1.1
53115  * Copyright(c) 2006-2007, Ext JS, LLC.
53116  *
53117  * Originally Released Under LGPL - original licence link has changed is not relivant.
53118  *
53119  * Fork - LGPL
53120  * <script type="text/javascript">
53121  */
53122  
53123  
53124 /*
53125  * Private internal class for reading and applying state
53126  */
53127 Roo.LayoutStateManager = function(layout){
53128      // default empty state
53129      this.state = {
53130         north: {},
53131         south: {},
53132         east: {},
53133         west: {}       
53134     };
53135 };
53136
53137 Roo.LayoutStateManager.prototype = {
53138     init : function(layout, provider){
53139         this.provider = provider;
53140         var state = provider.get(layout.id+"-layout-state");
53141         if(state){
53142             var wasUpdating = layout.isUpdating();
53143             if(!wasUpdating){
53144                 layout.beginUpdate();
53145             }
53146             for(var key in state){
53147                 if(typeof state[key] != "function"){
53148                     var rstate = state[key];
53149                     var r = layout.getRegion(key);
53150                     if(r && rstate){
53151                         if(rstate.size){
53152                             r.resizeTo(rstate.size);
53153                         }
53154                         if(rstate.collapsed == true){
53155                             r.collapse(true);
53156                         }else{
53157                             r.expand(null, true);
53158                         }
53159                     }
53160                 }
53161             }
53162             if(!wasUpdating){
53163                 layout.endUpdate();
53164             }
53165             this.state = state; 
53166         }
53167         this.layout = layout;
53168         layout.on("regionresized", this.onRegionResized, this);
53169         layout.on("regioncollapsed", this.onRegionCollapsed, this);
53170         layout.on("regionexpanded", this.onRegionExpanded, this);
53171     },
53172     
53173     storeState : function(){
53174         this.provider.set(this.layout.id+"-layout-state", this.state);
53175     },
53176     
53177     onRegionResized : function(region, newSize){
53178         this.state[region.getPosition()].size = newSize;
53179         this.storeState();
53180     },
53181     
53182     onRegionCollapsed : function(region){
53183         this.state[region.getPosition()].collapsed = true;
53184         this.storeState();
53185     },
53186     
53187     onRegionExpanded : function(region){
53188         this.state[region.getPosition()].collapsed = false;
53189         this.storeState();
53190     }
53191 };/*
53192  * Based on:
53193  * Ext JS Library 1.1.1
53194  * Copyright(c) 2006-2007, Ext JS, LLC.
53195  *
53196  * Originally Released Under LGPL - original licence link has changed is not relivant.
53197  *
53198  * Fork - LGPL
53199  * <script type="text/javascript">
53200  */
53201 /**
53202  * @class Roo.ContentPanel
53203  * @extends Roo.util.Observable
53204  * A basic ContentPanel element.
53205  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
53206  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
53207  * @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
53208  * @cfg {Boolean}   closable      True if the panel can be closed/removed
53209  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
53210  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
53211  * @cfg {Toolbar}   toolbar       A toolbar for this panel
53212  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
53213  * @cfg {String} title          The title for this panel
53214  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
53215  * @cfg {String} url            Calls {@link #setUrl} with this value
53216  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
53217  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
53218  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
53219  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
53220
53221  * @constructor
53222  * Create a new ContentPanel.
53223  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
53224  * @param {String/Object} config A string to set only the title or a config object
53225  * @param {String} content (optional) Set the HTML content for this panel
53226  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
53227  */
53228 Roo.ContentPanel = function(el, config, content){
53229     
53230      
53231     /*
53232     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
53233         config = el;
53234         el = Roo.id();
53235     }
53236     if (config && config.parentLayout) { 
53237         el = config.parentLayout.el.createChild(); 
53238     }
53239     */
53240     if(el.autoCreate){ // xtype is available if this is called from factory
53241         config = el;
53242         el = Roo.id();
53243     }
53244     this.el = Roo.get(el);
53245     if(!this.el && config && config.autoCreate){
53246         if(typeof config.autoCreate == "object"){
53247             if(!config.autoCreate.id){
53248                 config.autoCreate.id = config.id||el;
53249             }
53250             this.el = Roo.DomHelper.append(document.body,
53251                         config.autoCreate, true);
53252         }else{
53253             this.el = Roo.DomHelper.append(document.body,
53254                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
53255         }
53256     }
53257     this.closable = false;
53258     this.loaded = false;
53259     this.active = false;
53260     if(typeof config == "string"){
53261         this.title = config;
53262     }else{
53263         Roo.apply(this, config);
53264     }
53265     
53266     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
53267         this.wrapEl = this.el.wrap();
53268         this.toolbar.container = this.el.insertSibling(false, 'before');
53269         this.toolbar = new Roo.Toolbar(this.toolbar);
53270     }
53271     
53272     // xtype created footer. - not sure if will work as we normally have to render first..
53273     if (this.footer && !this.footer.el && this.footer.xtype) {
53274         if (!this.wrapEl) {
53275             this.wrapEl = this.el.wrap();
53276         }
53277     
53278         this.footer.container = this.wrapEl.createChild();
53279          
53280         this.footer = Roo.factory(this.footer, Roo);
53281         
53282     }
53283     
53284     if(this.resizeEl){
53285         this.resizeEl = Roo.get(this.resizeEl, true);
53286     }else{
53287         this.resizeEl = this.el;
53288     }
53289     // handle view.xtype
53290     
53291  
53292     
53293     
53294     this.addEvents({
53295         /**
53296          * @event activate
53297          * Fires when this panel is activated. 
53298          * @param {Roo.ContentPanel} this
53299          */
53300         "activate" : true,
53301         /**
53302          * @event deactivate
53303          * Fires when this panel is activated. 
53304          * @param {Roo.ContentPanel} this
53305          */
53306         "deactivate" : true,
53307
53308         /**
53309          * @event resize
53310          * Fires when this panel is resized if fitToFrame is true.
53311          * @param {Roo.ContentPanel} this
53312          * @param {Number} width The width after any component adjustments
53313          * @param {Number} height The height after any component adjustments
53314          */
53315         "resize" : true,
53316         
53317          /**
53318          * @event render
53319          * Fires when this tab is created
53320          * @param {Roo.ContentPanel} this
53321          */
53322         "render" : true
53323         
53324         
53325         
53326     });
53327     
53328
53329     
53330     
53331     if(this.autoScroll){
53332         this.resizeEl.setStyle("overflow", "auto");
53333     } else {
53334         // fix randome scrolling
53335         this.el.on('scroll', function() {
53336             Roo.log('fix random scolling');
53337             this.scrollTo('top',0); 
53338         });
53339     }
53340     content = content || this.content;
53341     if(content){
53342         this.setContent(content);
53343     }
53344     if(config && config.url){
53345         this.setUrl(this.url, this.params, this.loadOnce);
53346     }
53347     
53348     
53349     
53350     Roo.ContentPanel.superclass.constructor.call(this);
53351     
53352     if (this.view && typeof(this.view.xtype) != 'undefined') {
53353         this.view.el = this.el.appendChild(document.createElement("div"));
53354         this.view = Roo.factory(this.view); 
53355         this.view.render  &&  this.view.render(false, '');  
53356     }
53357     
53358     
53359     this.fireEvent('render', this);
53360 };
53361
53362 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
53363     tabTip:'',
53364     setRegion : function(region){
53365         this.region = region;
53366         if(region){
53367            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
53368         }else{
53369            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
53370         } 
53371     },
53372     
53373     /**
53374      * Returns the toolbar for this Panel if one was configured. 
53375      * @return {Roo.Toolbar} 
53376      */
53377     getToolbar : function(){
53378         return this.toolbar;
53379     },
53380     
53381     setActiveState : function(active){
53382         this.active = active;
53383         if(!active){
53384             this.fireEvent("deactivate", this);
53385         }else{
53386             this.fireEvent("activate", this);
53387         }
53388     },
53389     /**
53390      * Updates this panel's element
53391      * @param {String} content The new content
53392      * @param {Boolean} loadScripts (optional) true to look for and process scripts
53393     */
53394     setContent : function(content, loadScripts){
53395         this.el.update(content, loadScripts);
53396     },
53397
53398     ignoreResize : function(w, h){
53399         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
53400             return true;
53401         }else{
53402             this.lastSize = {width: w, height: h};
53403             return false;
53404         }
53405     },
53406     /**
53407      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
53408      * @return {Roo.UpdateManager} The UpdateManager
53409      */
53410     getUpdateManager : function(){
53411         return this.el.getUpdateManager();
53412     },
53413      /**
53414      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
53415      * @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:
53416 <pre><code>
53417 panel.load({
53418     url: "your-url.php",
53419     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
53420     callback: yourFunction,
53421     scope: yourObject, //(optional scope)
53422     discardUrl: false,
53423     nocache: false,
53424     text: "Loading...",
53425     timeout: 30,
53426     scripts: false
53427 });
53428 </code></pre>
53429      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
53430      * 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.
53431      * @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}
53432      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
53433      * @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.
53434      * @return {Roo.ContentPanel} this
53435      */
53436     load : function(){
53437         var um = this.el.getUpdateManager();
53438         um.update.apply(um, arguments);
53439         return this;
53440     },
53441
53442
53443     /**
53444      * 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.
53445      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
53446      * @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)
53447      * @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)
53448      * @return {Roo.UpdateManager} The UpdateManager
53449      */
53450     setUrl : function(url, params, loadOnce){
53451         if(this.refreshDelegate){
53452             this.removeListener("activate", this.refreshDelegate);
53453         }
53454         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
53455         this.on("activate", this.refreshDelegate);
53456         return this.el.getUpdateManager();
53457     },
53458     
53459     _handleRefresh : function(url, params, loadOnce){
53460         if(!loadOnce || !this.loaded){
53461             var updater = this.el.getUpdateManager();
53462             updater.update(url, params, this._setLoaded.createDelegate(this));
53463         }
53464     },
53465     
53466     _setLoaded : function(){
53467         this.loaded = true;
53468     }, 
53469     
53470     /**
53471      * Returns this panel's id
53472      * @return {String} 
53473      */
53474     getId : function(){
53475         return this.el.id;
53476     },
53477     
53478     /** 
53479      * Returns this panel's element - used by regiosn to add.
53480      * @return {Roo.Element} 
53481      */
53482     getEl : function(){
53483         return this.wrapEl || this.el;
53484     },
53485     
53486     adjustForComponents : function(width, height)
53487     {
53488         //Roo.log('adjustForComponents ');
53489         if(this.resizeEl != this.el){
53490             width -= this.el.getFrameWidth('lr');
53491             height -= this.el.getFrameWidth('tb');
53492         }
53493         if(this.toolbar){
53494             var te = this.toolbar.getEl();
53495             height -= te.getHeight();
53496             te.setWidth(width);
53497         }
53498         if(this.footer){
53499             var te = this.footer.getEl();
53500             Roo.log("footer:" + te.getHeight());
53501             
53502             height -= te.getHeight();
53503             te.setWidth(width);
53504         }
53505         
53506         
53507         if(this.adjustments){
53508             width += this.adjustments[0];
53509             height += this.adjustments[1];
53510         }
53511         return {"width": width, "height": height};
53512     },
53513     
53514     setSize : function(width, height){
53515         if(this.fitToFrame && !this.ignoreResize(width, height)){
53516             if(this.fitContainer && this.resizeEl != this.el){
53517                 this.el.setSize(width, height);
53518             }
53519             var size = this.adjustForComponents(width, height);
53520             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
53521             this.fireEvent('resize', this, size.width, size.height);
53522         }
53523     },
53524     
53525     /**
53526      * Returns this panel's title
53527      * @return {String} 
53528      */
53529     getTitle : function(){
53530         return this.title;
53531     },
53532     
53533     /**
53534      * Set this panel's title
53535      * @param {String} title
53536      */
53537     setTitle : function(title){
53538         this.title = title;
53539         if(this.region){
53540             this.region.updatePanelTitle(this, title);
53541         }
53542     },
53543     
53544     /**
53545      * Returns true is this panel was configured to be closable
53546      * @return {Boolean} 
53547      */
53548     isClosable : function(){
53549         return this.closable;
53550     },
53551     
53552     beforeSlide : function(){
53553         this.el.clip();
53554         this.resizeEl.clip();
53555     },
53556     
53557     afterSlide : function(){
53558         this.el.unclip();
53559         this.resizeEl.unclip();
53560     },
53561     
53562     /**
53563      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
53564      *   Will fail silently if the {@link #setUrl} method has not been called.
53565      *   This does not activate the panel, just updates its content.
53566      */
53567     refresh : function(){
53568         if(this.refreshDelegate){
53569            this.loaded = false;
53570            this.refreshDelegate();
53571         }
53572     },
53573     
53574     /**
53575      * Destroys this panel
53576      */
53577     destroy : function(){
53578         this.el.removeAllListeners();
53579         var tempEl = document.createElement("span");
53580         tempEl.appendChild(this.el.dom);
53581         tempEl.innerHTML = "";
53582         this.el.remove();
53583         this.el = null;
53584     },
53585     
53586     /**
53587      * form - if the content panel contains a form - this is a reference to it.
53588      * @type {Roo.form.Form}
53589      */
53590     form : false,
53591     /**
53592      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
53593      *    This contains a reference to it.
53594      * @type {Roo.View}
53595      */
53596     view : false,
53597     
53598       /**
53599      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
53600      * <pre><code>
53601
53602 layout.addxtype({
53603        xtype : 'Form',
53604        items: [ .... ]
53605    }
53606 );
53607
53608 </code></pre>
53609      * @param {Object} cfg Xtype definition of item to add.
53610      */
53611     
53612     addxtype : function(cfg) {
53613         // add form..
53614         if (cfg.xtype.match(/^Form$/)) {
53615             
53616             var el;
53617             //if (this.footer) {
53618             //    el = this.footer.container.insertSibling(false, 'before');
53619             //} else {
53620                 el = this.el.createChild();
53621             //}
53622
53623             this.form = new  Roo.form.Form(cfg);
53624             
53625             
53626             if ( this.form.allItems.length) {
53627                 this.form.render(el.dom);
53628             }
53629             return this.form;
53630         }
53631         // should only have one of theses..
53632         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
53633             // views.. should not be just added - used named prop 'view''
53634             
53635             cfg.el = this.el.appendChild(document.createElement("div"));
53636             // factory?
53637             
53638             var ret = new Roo.factory(cfg);
53639              
53640              ret.render && ret.render(false, ''); // render blank..
53641             this.view = ret;
53642             return ret;
53643         }
53644         return false;
53645     }
53646 });
53647
53648 /**
53649  * @class Roo.GridPanel
53650  * @extends Roo.ContentPanel
53651  * @constructor
53652  * Create a new GridPanel.
53653  * @param {Roo.grid.Grid} grid The grid for this panel
53654  * @param {String/Object} config A string to set only the panel's title, or a config object
53655  */
53656 Roo.GridPanel = function(grid, config){
53657     
53658   
53659     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
53660         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
53661         
53662     this.wrapper.dom.appendChild(grid.getGridEl().dom);
53663     
53664     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
53665     
53666     if(this.toolbar){
53667         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
53668     }
53669     // xtype created footer. - not sure if will work as we normally have to render first..
53670     if (this.footer && !this.footer.el && this.footer.xtype) {
53671         
53672         this.footer.container = this.grid.getView().getFooterPanel(true);
53673         this.footer.dataSource = this.grid.dataSource;
53674         this.footer = Roo.factory(this.footer, Roo);
53675         
53676     }
53677     
53678     grid.monitorWindowResize = false; // turn off autosizing
53679     grid.autoHeight = false;
53680     grid.autoWidth = false;
53681     this.grid = grid;
53682     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
53683 };
53684
53685 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
53686     getId : function(){
53687         return this.grid.id;
53688     },
53689     
53690     /**
53691      * Returns the grid for this panel
53692      * @return {Roo.grid.Grid} 
53693      */
53694     getGrid : function(){
53695         return this.grid;    
53696     },
53697     
53698     setSize : function(width, height){
53699         if(!this.ignoreResize(width, height)){
53700             var grid = this.grid;
53701             var size = this.adjustForComponents(width, height);
53702             grid.getGridEl().setSize(size.width, size.height);
53703             grid.autoSize();
53704         }
53705     },
53706     
53707     beforeSlide : function(){
53708         this.grid.getView().scroller.clip();
53709     },
53710     
53711     afterSlide : function(){
53712         this.grid.getView().scroller.unclip();
53713     },
53714     
53715     destroy : function(){
53716         this.grid.destroy();
53717         delete this.grid;
53718         Roo.GridPanel.superclass.destroy.call(this); 
53719     }
53720 });
53721
53722
53723 /**
53724  * @class Roo.NestedLayoutPanel
53725  * @extends Roo.ContentPanel
53726  * @constructor
53727  * Create a new NestedLayoutPanel.
53728  * 
53729  * 
53730  * @param {Roo.BorderLayout} layout The layout for this panel
53731  * @param {String/Object} config A string to set only the title or a config object
53732  */
53733 Roo.NestedLayoutPanel = function(layout, config)
53734 {
53735     // construct with only one argument..
53736     /* FIXME - implement nicer consturctors
53737     if (layout.layout) {
53738         config = layout;
53739         layout = config.layout;
53740         delete config.layout;
53741     }
53742     if (layout.xtype && !layout.getEl) {
53743         // then layout needs constructing..
53744         layout = Roo.factory(layout, Roo);
53745     }
53746     */
53747     
53748     
53749     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
53750     
53751     layout.monitorWindowResize = false; // turn off autosizing
53752     this.layout = layout;
53753     this.layout.getEl().addClass("x-layout-nested-layout");
53754     
53755     
53756     
53757     
53758 };
53759
53760 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
53761
53762     setSize : function(width, height){
53763         if(!this.ignoreResize(width, height)){
53764             var size = this.adjustForComponents(width, height);
53765             var el = this.layout.getEl();
53766             el.setSize(size.width, size.height);
53767             var touch = el.dom.offsetWidth;
53768             this.layout.layout();
53769             // ie requires a double layout on the first pass
53770             if(Roo.isIE && !this.initialized){
53771                 this.initialized = true;
53772                 this.layout.layout();
53773             }
53774         }
53775     },
53776     
53777     // activate all subpanels if not currently active..
53778     
53779     setActiveState : function(active){
53780         this.active = active;
53781         if(!active){
53782             this.fireEvent("deactivate", this);
53783             return;
53784         }
53785         
53786         this.fireEvent("activate", this);
53787         // not sure if this should happen before or after..
53788         if (!this.layout) {
53789             return; // should not happen..
53790         }
53791         var reg = false;
53792         for (var r in this.layout.regions) {
53793             reg = this.layout.getRegion(r);
53794             if (reg.getActivePanel()) {
53795                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
53796                 reg.setActivePanel(reg.getActivePanel());
53797                 continue;
53798             }
53799             if (!reg.panels.length) {
53800                 continue;
53801             }
53802             reg.showPanel(reg.getPanel(0));
53803         }
53804         
53805         
53806         
53807         
53808     },
53809     
53810     /**
53811      * Returns the nested BorderLayout for this panel
53812      * @return {Roo.BorderLayout} 
53813      */
53814     getLayout : function(){
53815         return this.layout;
53816     },
53817     
53818      /**
53819      * Adds a xtype elements to the layout of the nested panel
53820      * <pre><code>
53821
53822 panel.addxtype({
53823        xtype : 'ContentPanel',
53824        region: 'west',
53825        items: [ .... ]
53826    }
53827 );
53828
53829 panel.addxtype({
53830         xtype : 'NestedLayoutPanel',
53831         region: 'west',
53832         layout: {
53833            center: { },
53834            west: { }   
53835         },
53836         items : [ ... list of content panels or nested layout panels.. ]
53837    }
53838 );
53839 </code></pre>
53840      * @param {Object} cfg Xtype definition of item to add.
53841      */
53842     addxtype : function(cfg) {
53843         return this.layout.addxtype(cfg);
53844     
53845     }
53846 });
53847
53848 Roo.ScrollPanel = function(el, config, content){
53849     config = config || {};
53850     config.fitToFrame = true;
53851     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
53852     
53853     this.el.dom.style.overflow = "hidden";
53854     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
53855     this.el.removeClass("x-layout-inactive-content");
53856     this.el.on("mousewheel", this.onWheel, this);
53857
53858     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
53859     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
53860     up.unselectable(); down.unselectable();
53861     up.on("click", this.scrollUp, this);
53862     down.on("click", this.scrollDown, this);
53863     up.addClassOnOver("x-scroller-btn-over");
53864     down.addClassOnOver("x-scroller-btn-over");
53865     up.addClassOnClick("x-scroller-btn-click");
53866     down.addClassOnClick("x-scroller-btn-click");
53867     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
53868
53869     this.resizeEl = this.el;
53870     this.el = wrap; this.up = up; this.down = down;
53871 };
53872
53873 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
53874     increment : 100,
53875     wheelIncrement : 5,
53876     scrollUp : function(){
53877         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
53878     },
53879
53880     scrollDown : function(){
53881         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
53882     },
53883
53884     afterScroll : function(){
53885         var el = this.resizeEl;
53886         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
53887         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53888         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53889     },
53890
53891     setSize : function(){
53892         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
53893         this.afterScroll();
53894     },
53895
53896     onWheel : function(e){
53897         var d = e.getWheelDelta();
53898         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
53899         this.afterScroll();
53900         e.stopEvent();
53901     },
53902
53903     setContent : function(content, loadScripts){
53904         this.resizeEl.update(content, loadScripts);
53905     }
53906
53907 });
53908
53909
53910
53911
53912
53913
53914
53915
53916
53917 /**
53918  * @class Roo.TreePanel
53919  * @extends Roo.ContentPanel
53920  * @constructor
53921  * Create a new TreePanel. - defaults to fit/scoll contents.
53922  * @param {String/Object} config A string to set only the panel's title, or a config object
53923  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
53924  */
53925 Roo.TreePanel = function(config){
53926     var el = config.el;
53927     var tree = config.tree;
53928     delete config.tree; 
53929     delete config.el; // hopefull!
53930     
53931     // wrapper for IE7 strict & safari scroll issue
53932     
53933     var treeEl = el.createChild();
53934     config.resizeEl = treeEl;
53935     
53936     
53937     
53938     Roo.TreePanel.superclass.constructor.call(this, el, config);
53939  
53940  
53941     this.tree = new Roo.tree.TreePanel(treeEl , tree);
53942     //console.log(tree);
53943     this.on('activate', function()
53944     {
53945         if (this.tree.rendered) {
53946             return;
53947         }
53948         //console.log('render tree');
53949         this.tree.render();
53950     });
53951     // this should not be needed.. - it's actually the 'el' that resizes?
53952     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
53953     
53954     //this.on('resize',  function (cp, w, h) {
53955     //        this.tree.innerCt.setWidth(w);
53956     //        this.tree.innerCt.setHeight(h);
53957     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
53958     //});
53959
53960         
53961     
53962 };
53963
53964 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
53965     fitToFrame : true,
53966     autoScroll : true
53967 });
53968
53969
53970
53971
53972
53973
53974
53975
53976
53977
53978
53979 /*
53980  * Based on:
53981  * Ext JS Library 1.1.1
53982  * Copyright(c) 2006-2007, Ext JS, LLC.
53983  *
53984  * Originally Released Under LGPL - original licence link has changed is not relivant.
53985  *
53986  * Fork - LGPL
53987  * <script type="text/javascript">
53988  */
53989  
53990
53991 /**
53992  * @class Roo.ReaderLayout
53993  * @extends Roo.BorderLayout
53994  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
53995  * center region containing two nested regions (a top one for a list view and one for item preview below),
53996  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
53997  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
53998  * expedites the setup of the overall layout and regions for this common application style.
53999  * Example:
54000  <pre><code>
54001 var reader = new Roo.ReaderLayout();
54002 var CP = Roo.ContentPanel;  // shortcut for adding
54003
54004 reader.beginUpdate();
54005 reader.add("north", new CP("north", "North"));
54006 reader.add("west", new CP("west", {title: "West"}));
54007 reader.add("east", new CP("east", {title: "East"}));
54008
54009 reader.regions.listView.add(new CP("listView", "List"));
54010 reader.regions.preview.add(new CP("preview", "Preview"));
54011 reader.endUpdate();
54012 </code></pre>
54013 * @constructor
54014 * Create a new ReaderLayout
54015 * @param {Object} config Configuration options
54016 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
54017 * document.body if omitted)
54018 */
54019 Roo.ReaderLayout = function(config, renderTo){
54020     var c = config || {size:{}};
54021     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
54022         north: c.north !== false ? Roo.apply({
54023             split:false,
54024             initialSize: 32,
54025             titlebar: false
54026         }, c.north) : false,
54027         west: c.west !== false ? Roo.apply({
54028             split:true,
54029             initialSize: 200,
54030             minSize: 175,
54031             maxSize: 400,
54032             titlebar: true,
54033             collapsible: true,
54034             animate: true,
54035             margins:{left:5,right:0,bottom:5,top:5},
54036             cmargins:{left:5,right:5,bottom:5,top:5}
54037         }, c.west) : false,
54038         east: c.east !== false ? Roo.apply({
54039             split:true,
54040             initialSize: 200,
54041             minSize: 175,
54042             maxSize: 400,
54043             titlebar: true,
54044             collapsible: true,
54045             animate: true,
54046             margins:{left:0,right:5,bottom:5,top:5},
54047             cmargins:{left:5,right:5,bottom:5,top:5}
54048         }, c.east) : false,
54049         center: Roo.apply({
54050             tabPosition: 'top',
54051             autoScroll:false,
54052             closeOnTab: true,
54053             titlebar:false,
54054             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
54055         }, c.center)
54056     });
54057
54058     this.el.addClass('x-reader');
54059
54060     this.beginUpdate();
54061
54062     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
54063         south: c.preview !== false ? Roo.apply({
54064             split:true,
54065             initialSize: 200,
54066             minSize: 100,
54067             autoScroll:true,
54068             collapsible:true,
54069             titlebar: true,
54070             cmargins:{top:5,left:0, right:0, bottom:0}
54071         }, c.preview) : false,
54072         center: Roo.apply({
54073             autoScroll:false,
54074             titlebar:false,
54075             minHeight:200
54076         }, c.listView)
54077     });
54078     this.add('center', new Roo.NestedLayoutPanel(inner,
54079             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
54080
54081     this.endUpdate();
54082
54083     this.regions.preview = inner.getRegion('south');
54084     this.regions.listView = inner.getRegion('center');
54085 };
54086
54087 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
54088  * Based on:
54089  * Ext JS Library 1.1.1
54090  * Copyright(c) 2006-2007, Ext JS, LLC.
54091  *
54092  * Originally Released Under LGPL - original licence link has changed is not relivant.
54093  *
54094  * Fork - LGPL
54095  * <script type="text/javascript">
54096  */
54097  
54098 /**
54099  * @class Roo.grid.Grid
54100  * @extends Roo.util.Observable
54101  * This class represents the primary interface of a component based grid control.
54102  * <br><br>Usage:<pre><code>
54103  var grid = new Roo.grid.Grid("my-container-id", {
54104      ds: myDataStore,
54105      cm: myColModel,
54106      selModel: mySelectionModel,
54107      autoSizeColumns: true,
54108      monitorWindowResize: false,
54109      trackMouseOver: true
54110  });
54111  // set any options
54112  grid.render();
54113  * </code></pre>
54114  * <b>Common Problems:</b><br/>
54115  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
54116  * element will correct this<br/>
54117  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
54118  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
54119  * are unpredictable.<br/>
54120  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
54121  * grid to calculate dimensions/offsets.<br/>
54122   * @constructor
54123  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
54124  * The container MUST have some type of size defined for the grid to fill. The container will be
54125  * automatically set to position relative if it isn't already.
54126  * @param {Object} config A config object that sets properties on this grid.
54127  */
54128 Roo.grid.Grid = function(container, config){
54129         // initialize the container
54130         this.container = Roo.get(container);
54131         this.container.update("");
54132         this.container.setStyle("overflow", "hidden");
54133     this.container.addClass('x-grid-container');
54134
54135     this.id = this.container.id;
54136
54137     Roo.apply(this, config);
54138     // check and correct shorthanded configs
54139     if(this.ds){
54140         this.dataSource = this.ds;
54141         delete this.ds;
54142     }
54143     if(this.cm){
54144         this.colModel = this.cm;
54145         delete this.cm;
54146     }
54147     if(this.sm){
54148         this.selModel = this.sm;
54149         delete this.sm;
54150     }
54151
54152     if (this.selModel) {
54153         this.selModel = Roo.factory(this.selModel, Roo.grid);
54154         this.sm = this.selModel;
54155         this.sm.xmodule = this.xmodule || false;
54156     }
54157     if (typeof(this.colModel.config) == 'undefined') {
54158         this.colModel = new Roo.grid.ColumnModel(this.colModel);
54159         this.cm = this.colModel;
54160         this.cm.xmodule = this.xmodule || false;
54161     }
54162     if (this.dataSource) {
54163         this.dataSource= Roo.factory(this.dataSource, Roo.data);
54164         this.ds = this.dataSource;
54165         this.ds.xmodule = this.xmodule || false;
54166          
54167     }
54168     
54169     
54170     
54171     if(this.width){
54172         this.container.setWidth(this.width);
54173     }
54174
54175     if(this.height){
54176         this.container.setHeight(this.height);
54177     }
54178     /** @private */
54179         this.addEvents({
54180         // raw events
54181         /**
54182          * @event click
54183          * The raw click event for the entire grid.
54184          * @param {Roo.EventObject} e
54185          */
54186         "click" : true,
54187         /**
54188          * @event dblclick
54189          * The raw dblclick event for the entire grid.
54190          * @param {Roo.EventObject} e
54191          */
54192         "dblclick" : true,
54193         /**
54194          * @event contextmenu
54195          * The raw contextmenu event for the entire grid.
54196          * @param {Roo.EventObject} e
54197          */
54198         "contextmenu" : true,
54199         /**
54200          * @event mousedown
54201          * The raw mousedown event for the entire grid.
54202          * @param {Roo.EventObject} e
54203          */
54204         "mousedown" : true,
54205         /**
54206          * @event mouseup
54207          * The raw mouseup event for the entire grid.
54208          * @param {Roo.EventObject} e
54209          */
54210         "mouseup" : true,
54211         /**
54212          * @event mouseover
54213          * The raw mouseover event for the entire grid.
54214          * @param {Roo.EventObject} e
54215          */
54216         "mouseover" : true,
54217         /**
54218          * @event mouseout
54219          * The raw mouseout event for the entire grid.
54220          * @param {Roo.EventObject} e
54221          */
54222         "mouseout" : true,
54223         /**
54224          * @event keypress
54225          * The raw keypress event for the entire grid.
54226          * @param {Roo.EventObject} e
54227          */
54228         "keypress" : true,
54229         /**
54230          * @event keydown
54231          * The raw keydown event for the entire grid.
54232          * @param {Roo.EventObject} e
54233          */
54234         "keydown" : true,
54235
54236         // custom events
54237
54238         /**
54239          * @event cellclick
54240          * Fires when a cell is clicked
54241          * @param {Grid} this
54242          * @param {Number} rowIndex
54243          * @param {Number} columnIndex
54244          * @param {Roo.EventObject} e
54245          */
54246         "cellclick" : true,
54247         /**
54248          * @event celldblclick
54249          * Fires when a cell is double clicked
54250          * @param {Grid} this
54251          * @param {Number} rowIndex
54252          * @param {Number} columnIndex
54253          * @param {Roo.EventObject} e
54254          */
54255         "celldblclick" : true,
54256         /**
54257          * @event rowclick
54258          * Fires when a row is clicked
54259          * @param {Grid} this
54260          * @param {Number} rowIndex
54261          * @param {Roo.EventObject} e
54262          */
54263         "rowclick" : true,
54264         /**
54265          * @event rowdblclick
54266          * Fires when a row is double clicked
54267          * @param {Grid} this
54268          * @param {Number} rowIndex
54269          * @param {Roo.EventObject} e
54270          */
54271         "rowdblclick" : true,
54272         /**
54273          * @event headerclick
54274          * Fires when a header is clicked
54275          * @param {Grid} this
54276          * @param {Number} columnIndex
54277          * @param {Roo.EventObject} e
54278          */
54279         "headerclick" : true,
54280         /**
54281          * @event headerdblclick
54282          * Fires when a header cell is double clicked
54283          * @param {Grid} this
54284          * @param {Number} columnIndex
54285          * @param {Roo.EventObject} e
54286          */
54287         "headerdblclick" : true,
54288         /**
54289          * @event rowcontextmenu
54290          * Fires when a row is right clicked
54291          * @param {Grid} this
54292          * @param {Number} rowIndex
54293          * @param {Roo.EventObject} e
54294          */
54295         "rowcontextmenu" : true,
54296         /**
54297          * @event cellcontextmenu
54298          * Fires when a cell is right clicked
54299          * @param {Grid} this
54300          * @param {Number} rowIndex
54301          * @param {Number} cellIndex
54302          * @param {Roo.EventObject} e
54303          */
54304          "cellcontextmenu" : true,
54305         /**
54306          * @event headercontextmenu
54307          * Fires when a header is right clicked
54308          * @param {Grid} this
54309          * @param {Number} columnIndex
54310          * @param {Roo.EventObject} e
54311          */
54312         "headercontextmenu" : true,
54313         /**
54314          * @event bodyscroll
54315          * Fires when the body element is scrolled
54316          * @param {Number} scrollLeft
54317          * @param {Number} scrollTop
54318          */
54319         "bodyscroll" : true,
54320         /**
54321          * @event columnresize
54322          * Fires when the user resizes a column
54323          * @param {Number} columnIndex
54324          * @param {Number} newSize
54325          */
54326         "columnresize" : true,
54327         /**
54328          * @event columnmove
54329          * Fires when the user moves a column
54330          * @param {Number} oldIndex
54331          * @param {Number} newIndex
54332          */
54333         "columnmove" : true,
54334         /**
54335          * @event startdrag
54336          * Fires when row(s) start being dragged
54337          * @param {Grid} this
54338          * @param {Roo.GridDD} dd The drag drop object
54339          * @param {event} e The raw browser event
54340          */
54341         "startdrag" : true,
54342         /**
54343          * @event enddrag
54344          * Fires when a drag operation is complete
54345          * @param {Grid} this
54346          * @param {Roo.GridDD} dd The drag drop object
54347          * @param {event} e The raw browser event
54348          */
54349         "enddrag" : true,
54350         /**
54351          * @event dragdrop
54352          * Fires when dragged row(s) are dropped on a valid DD target
54353          * @param {Grid} this
54354          * @param {Roo.GridDD} dd The drag drop object
54355          * @param {String} targetId The target drag drop object
54356          * @param {event} e The raw browser event
54357          */
54358         "dragdrop" : true,
54359         /**
54360          * @event dragover
54361          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
54362          * @param {Grid} this
54363          * @param {Roo.GridDD} dd The drag drop object
54364          * @param {String} targetId The target drag drop object
54365          * @param {event} e The raw browser event
54366          */
54367         "dragover" : true,
54368         /**
54369          * @event dragenter
54370          *  Fires when the dragged row(s) first cross another DD target while being dragged
54371          * @param {Grid} this
54372          * @param {Roo.GridDD} dd The drag drop object
54373          * @param {String} targetId The target drag drop object
54374          * @param {event} e The raw browser event
54375          */
54376         "dragenter" : true,
54377         /**
54378          * @event dragout
54379          * Fires when the dragged row(s) leave another DD target while being dragged
54380          * @param {Grid} this
54381          * @param {Roo.GridDD} dd The drag drop object
54382          * @param {String} targetId The target drag drop object
54383          * @param {event} e The raw browser event
54384          */
54385         "dragout" : true,
54386         /**
54387          * @event rowclass
54388          * Fires when a row is rendered, so you can change add a style to it.
54389          * @param {GridView} gridview   The grid view
54390          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
54391          */
54392         'rowclass' : true,
54393
54394         /**
54395          * @event render
54396          * Fires when the grid is rendered
54397          * @param {Grid} grid
54398          */
54399         'render' : true
54400     });
54401
54402     Roo.grid.Grid.superclass.constructor.call(this);
54403 };
54404 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
54405     
54406     /**
54407      * @cfg {String} ddGroup - drag drop group.
54408      */
54409
54410     /**
54411      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
54412      */
54413     minColumnWidth : 25,
54414
54415     /**
54416      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
54417      * <b>on initial render.</b> It is more efficient to explicitly size the columns
54418      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
54419      */
54420     autoSizeColumns : false,
54421
54422     /**
54423      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
54424      */
54425     autoSizeHeaders : true,
54426
54427     /**
54428      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
54429      */
54430     monitorWindowResize : true,
54431
54432     /**
54433      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
54434      * rows measured to get a columns size. Default is 0 (all rows).
54435      */
54436     maxRowsToMeasure : 0,
54437
54438     /**
54439      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
54440      */
54441     trackMouseOver : true,
54442
54443     /**
54444     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
54445     */
54446     
54447     /**
54448     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
54449     */
54450     enableDragDrop : false,
54451     
54452     /**
54453     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
54454     */
54455     enableColumnMove : true,
54456     
54457     /**
54458     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
54459     */
54460     enableColumnHide : true,
54461     
54462     /**
54463     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
54464     */
54465     enableRowHeightSync : false,
54466     
54467     /**
54468     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
54469     */
54470     stripeRows : true,
54471     
54472     /**
54473     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
54474     */
54475     autoHeight : false,
54476
54477     /**
54478      * @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.
54479      */
54480     autoExpandColumn : false,
54481
54482     /**
54483     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
54484     * Default is 50.
54485     */
54486     autoExpandMin : 50,
54487
54488     /**
54489     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
54490     */
54491     autoExpandMax : 1000,
54492
54493     /**
54494     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
54495     */
54496     view : null,
54497
54498     /**
54499     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
54500     */
54501     loadMask : false,
54502     /**
54503     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
54504     */
54505     dropTarget: false,
54506     
54507    
54508     
54509     // private
54510     rendered : false,
54511
54512     /**
54513     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
54514     * of a fixed width. Default is false.
54515     */
54516     /**
54517     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
54518     */
54519     /**
54520      * Called once after all setup has been completed and the grid is ready to be rendered.
54521      * @return {Roo.grid.Grid} this
54522      */
54523     render : function()
54524     {
54525         var c = this.container;
54526         // try to detect autoHeight/width mode
54527         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
54528             this.autoHeight = true;
54529         }
54530         var view = this.getView();
54531         view.init(this);
54532
54533         c.on("click", this.onClick, this);
54534         c.on("dblclick", this.onDblClick, this);
54535         c.on("contextmenu", this.onContextMenu, this);
54536         c.on("keydown", this.onKeyDown, this);
54537         if (Roo.isTouch) {
54538             c.on("touchstart", this.onTouchStart, this);
54539         }
54540
54541         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
54542
54543         this.getSelectionModel().init(this);
54544
54545         view.render();
54546
54547         if(this.loadMask){
54548             this.loadMask = new Roo.LoadMask(this.container,
54549                     Roo.apply({store:this.dataSource}, this.loadMask));
54550         }
54551         
54552         
54553         if (this.toolbar && this.toolbar.xtype) {
54554             this.toolbar.container = this.getView().getHeaderPanel(true);
54555             this.toolbar = new Roo.Toolbar(this.toolbar);
54556         }
54557         if (this.footer && this.footer.xtype) {
54558             this.footer.dataSource = this.getDataSource();
54559             this.footer.container = this.getView().getFooterPanel(true);
54560             this.footer = Roo.factory(this.footer, Roo);
54561         }
54562         if (this.dropTarget && this.dropTarget.xtype) {
54563             delete this.dropTarget.xtype;
54564             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
54565         }
54566         
54567         
54568         this.rendered = true;
54569         this.fireEvent('render', this);
54570         return this;
54571     },
54572
54573         /**
54574          * Reconfigures the grid to use a different Store and Column Model.
54575          * The View will be bound to the new objects and refreshed.
54576          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
54577          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
54578          */
54579     reconfigure : function(dataSource, colModel){
54580         if(this.loadMask){
54581             this.loadMask.destroy();
54582             this.loadMask = new Roo.LoadMask(this.container,
54583                     Roo.apply({store:dataSource}, this.loadMask));
54584         }
54585         this.view.bind(dataSource, colModel);
54586         this.dataSource = dataSource;
54587         this.colModel = colModel;
54588         this.view.refresh(true);
54589     },
54590
54591     // private
54592     onKeyDown : function(e){
54593         this.fireEvent("keydown", e);
54594     },
54595
54596     /**
54597      * Destroy this grid.
54598      * @param {Boolean} removeEl True to remove the element
54599      */
54600     destroy : function(removeEl, keepListeners){
54601         if(this.loadMask){
54602             this.loadMask.destroy();
54603         }
54604         var c = this.container;
54605         c.removeAllListeners();
54606         this.view.destroy();
54607         this.colModel.purgeListeners();
54608         if(!keepListeners){
54609             this.purgeListeners();
54610         }
54611         c.update("");
54612         if(removeEl === true){
54613             c.remove();
54614         }
54615     },
54616
54617     // private
54618     processEvent : function(name, e){
54619         // does this fire select???
54620         //Roo.log('grid:processEvent '  + name);
54621         
54622         if (name != 'touchstart' ) {
54623             this.fireEvent(name, e);    
54624         }
54625         
54626         var t = e.getTarget();
54627         var v = this.view;
54628         var header = v.findHeaderIndex(t);
54629         if(header !== false){
54630             var ename = name == 'touchstart' ? 'click' : name;
54631              
54632             this.fireEvent("header" + ename, this, header, e);
54633         }else{
54634             var row = v.findRowIndex(t);
54635             var cell = v.findCellIndex(t);
54636             if (name == 'touchstart') {
54637                 // first touch is always a click.
54638                 // hopefull this happens after selection is updated.?
54639                 name = false;
54640                 
54641                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
54642                     var cs = this.selModel.getSelectedCell();
54643                     if (row == cs[0] && cell == cs[1]){
54644                         name = 'dblclick';
54645                     }
54646                 }
54647                 if (typeof(this.selModel.getSelections) != 'undefined') {
54648                     var cs = this.selModel.getSelections();
54649                     var ds = this.dataSource;
54650                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
54651                         name = 'dblclick';
54652                     }
54653                 }
54654                 if (!name) {
54655                     return;
54656                 }
54657             }
54658             
54659             
54660             if(row !== false){
54661                 this.fireEvent("row" + name, this, row, e);
54662                 if(cell !== false){
54663                     this.fireEvent("cell" + name, this, row, cell, e);
54664                 }
54665             }
54666         }
54667     },
54668
54669     // private
54670     onClick : function(e){
54671         this.processEvent("click", e);
54672     },
54673    // private
54674     onTouchStart : function(e){
54675         this.processEvent("touchstart", e);
54676     },
54677
54678     // private
54679     onContextMenu : function(e, t){
54680         this.processEvent("contextmenu", e);
54681     },
54682
54683     // private
54684     onDblClick : function(e){
54685         this.processEvent("dblclick", e);
54686     },
54687
54688     // private
54689     walkCells : function(row, col, step, fn, scope){
54690         var cm = this.colModel, clen = cm.getColumnCount();
54691         var ds = this.dataSource, rlen = ds.getCount(), first = true;
54692         if(step < 0){
54693             if(col < 0){
54694                 row--;
54695                 first = false;
54696             }
54697             while(row >= 0){
54698                 if(!first){
54699                     col = clen-1;
54700                 }
54701                 first = false;
54702                 while(col >= 0){
54703                     if(fn.call(scope || this, row, col, cm) === true){
54704                         return [row, col];
54705                     }
54706                     col--;
54707                 }
54708                 row--;
54709             }
54710         } else {
54711             if(col >= clen){
54712                 row++;
54713                 first = false;
54714             }
54715             while(row < rlen){
54716                 if(!first){
54717                     col = 0;
54718                 }
54719                 first = false;
54720                 while(col < clen){
54721                     if(fn.call(scope || this, row, col, cm) === true){
54722                         return [row, col];
54723                     }
54724                     col++;
54725                 }
54726                 row++;
54727             }
54728         }
54729         return null;
54730     },
54731
54732     // private
54733     getSelections : function(){
54734         return this.selModel.getSelections();
54735     },
54736
54737     /**
54738      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
54739      * but if manual update is required this method will initiate it.
54740      */
54741     autoSize : function(){
54742         if(this.rendered){
54743             this.view.layout();
54744             if(this.view.adjustForScroll){
54745                 this.view.adjustForScroll();
54746             }
54747         }
54748     },
54749
54750     /**
54751      * Returns the grid's underlying element.
54752      * @return {Element} The element
54753      */
54754     getGridEl : function(){
54755         return this.container;
54756     },
54757
54758     // private for compatibility, overridden by editor grid
54759     stopEditing : function(){},
54760
54761     /**
54762      * Returns the grid's SelectionModel.
54763      * @return {SelectionModel}
54764      */
54765     getSelectionModel : function(){
54766         if(!this.selModel){
54767             this.selModel = new Roo.grid.RowSelectionModel();
54768         }
54769         return this.selModel;
54770     },
54771
54772     /**
54773      * Returns the grid's DataSource.
54774      * @return {DataSource}
54775      */
54776     getDataSource : function(){
54777         return this.dataSource;
54778     },
54779
54780     /**
54781      * Returns the grid's ColumnModel.
54782      * @return {ColumnModel}
54783      */
54784     getColumnModel : function(){
54785         return this.colModel;
54786     },
54787
54788     /**
54789      * Returns the grid's GridView object.
54790      * @return {GridView}
54791      */
54792     getView : function(){
54793         if(!this.view){
54794             this.view = new Roo.grid.GridView(this.viewConfig);
54795         }
54796         return this.view;
54797     },
54798     /**
54799      * Called to get grid's drag proxy text, by default returns this.ddText.
54800      * @return {String}
54801      */
54802     getDragDropText : function(){
54803         var count = this.selModel.getCount();
54804         return String.format(this.ddText, count, count == 1 ? '' : 's');
54805     }
54806 });
54807 /**
54808  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
54809  * %0 is replaced with the number of selected rows.
54810  * @type String
54811  */
54812 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
54813  * Based on:
54814  * Ext JS Library 1.1.1
54815  * Copyright(c) 2006-2007, Ext JS, LLC.
54816  *
54817  * Originally Released Under LGPL - original licence link has changed is not relivant.
54818  *
54819  * Fork - LGPL
54820  * <script type="text/javascript">
54821  */
54822  
54823 Roo.grid.AbstractGridView = function(){
54824         this.grid = null;
54825         
54826         this.events = {
54827             "beforerowremoved" : true,
54828             "beforerowsinserted" : true,
54829             "beforerefresh" : true,
54830             "rowremoved" : true,
54831             "rowsinserted" : true,
54832             "rowupdated" : true,
54833             "refresh" : true
54834         };
54835     Roo.grid.AbstractGridView.superclass.constructor.call(this);
54836 };
54837
54838 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
54839     rowClass : "x-grid-row",
54840     cellClass : "x-grid-cell",
54841     tdClass : "x-grid-td",
54842     hdClass : "x-grid-hd",
54843     splitClass : "x-grid-hd-split",
54844     
54845     init: function(grid){
54846         this.grid = grid;
54847                 var cid = this.grid.getGridEl().id;
54848         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
54849         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
54850         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
54851         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
54852         },
54853         
54854     getColumnRenderers : function(){
54855         var renderers = [];
54856         var cm = this.grid.colModel;
54857         var colCount = cm.getColumnCount();
54858         for(var i = 0; i < colCount; i++){
54859             renderers[i] = cm.getRenderer(i);
54860         }
54861         return renderers;
54862     },
54863     
54864     getColumnIds : function(){
54865         var ids = [];
54866         var cm = this.grid.colModel;
54867         var colCount = cm.getColumnCount();
54868         for(var i = 0; i < colCount; i++){
54869             ids[i] = cm.getColumnId(i);
54870         }
54871         return ids;
54872     },
54873     
54874     getDataIndexes : function(){
54875         if(!this.indexMap){
54876             this.indexMap = this.buildIndexMap();
54877         }
54878         return this.indexMap.colToData;
54879     },
54880     
54881     getColumnIndexByDataIndex : function(dataIndex){
54882         if(!this.indexMap){
54883             this.indexMap = this.buildIndexMap();
54884         }
54885         return this.indexMap.dataToCol[dataIndex];
54886     },
54887     
54888     /**
54889      * Set a css style for a column dynamically. 
54890      * @param {Number} colIndex The index of the column
54891      * @param {String} name The css property name
54892      * @param {String} value The css value
54893      */
54894     setCSSStyle : function(colIndex, name, value){
54895         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
54896         Roo.util.CSS.updateRule(selector, name, value);
54897     },
54898     
54899     generateRules : function(cm){
54900         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
54901         Roo.util.CSS.removeStyleSheet(rulesId);
54902         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54903             var cid = cm.getColumnId(i);
54904             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
54905                          this.tdSelector, cid, " {\n}\n",
54906                          this.hdSelector, cid, " {\n}\n",
54907                          this.splitSelector, cid, " {\n}\n");
54908         }
54909         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54910     }
54911 });/*
54912  * Based on:
54913  * Ext JS Library 1.1.1
54914  * Copyright(c) 2006-2007, Ext JS, LLC.
54915  *
54916  * Originally Released Under LGPL - original licence link has changed is not relivant.
54917  *
54918  * Fork - LGPL
54919  * <script type="text/javascript">
54920  */
54921
54922 // private
54923 // This is a support class used internally by the Grid components
54924 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
54925     this.grid = grid;
54926     this.view = grid.getView();
54927     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54928     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
54929     if(hd2){
54930         this.setHandleElId(Roo.id(hd));
54931         this.setOuterHandleElId(Roo.id(hd2));
54932     }
54933     this.scroll = false;
54934 };
54935 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
54936     maxDragWidth: 120,
54937     getDragData : function(e){
54938         var t = Roo.lib.Event.getTarget(e);
54939         var h = this.view.findHeaderCell(t);
54940         if(h){
54941             return {ddel: h.firstChild, header:h};
54942         }
54943         return false;
54944     },
54945
54946     onInitDrag : function(e){
54947         this.view.headersDisabled = true;
54948         var clone = this.dragData.ddel.cloneNode(true);
54949         clone.id = Roo.id();
54950         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
54951         this.proxy.update(clone);
54952         return true;
54953     },
54954
54955     afterValidDrop : function(){
54956         var v = this.view;
54957         setTimeout(function(){
54958             v.headersDisabled = false;
54959         }, 50);
54960     },
54961
54962     afterInvalidDrop : function(){
54963         var v = this.view;
54964         setTimeout(function(){
54965             v.headersDisabled = false;
54966         }, 50);
54967     }
54968 });
54969 /*
54970  * Based on:
54971  * Ext JS Library 1.1.1
54972  * Copyright(c) 2006-2007, Ext JS, LLC.
54973  *
54974  * Originally Released Under LGPL - original licence link has changed is not relivant.
54975  *
54976  * Fork - LGPL
54977  * <script type="text/javascript">
54978  */
54979 // private
54980 // This is a support class used internally by the Grid components
54981 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
54982     this.grid = grid;
54983     this.view = grid.getView();
54984     // split the proxies so they don't interfere with mouse events
54985     this.proxyTop = Roo.DomHelper.append(document.body, {
54986         cls:"col-move-top", html:"&#160;"
54987     }, true);
54988     this.proxyBottom = Roo.DomHelper.append(document.body, {
54989         cls:"col-move-bottom", html:"&#160;"
54990     }, true);
54991     this.proxyTop.hide = this.proxyBottom.hide = function(){
54992         this.setLeftTop(-100,-100);
54993         this.setStyle("visibility", "hidden");
54994     };
54995     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54996     // temporarily disabled
54997     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
54998     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
54999 };
55000 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
55001     proxyOffsets : [-4, -9],
55002     fly: Roo.Element.fly,
55003
55004     getTargetFromEvent : function(e){
55005         var t = Roo.lib.Event.getTarget(e);
55006         var cindex = this.view.findCellIndex(t);
55007         if(cindex !== false){
55008             return this.view.getHeaderCell(cindex);
55009         }
55010         return null;
55011     },
55012
55013     nextVisible : function(h){
55014         var v = this.view, cm = this.grid.colModel;
55015         h = h.nextSibling;
55016         while(h){
55017             if(!cm.isHidden(v.getCellIndex(h))){
55018                 return h;
55019             }
55020             h = h.nextSibling;
55021         }
55022         return null;
55023     },
55024
55025     prevVisible : function(h){
55026         var v = this.view, cm = this.grid.colModel;
55027         h = h.prevSibling;
55028         while(h){
55029             if(!cm.isHidden(v.getCellIndex(h))){
55030                 return h;
55031             }
55032             h = h.prevSibling;
55033         }
55034         return null;
55035     },
55036
55037     positionIndicator : function(h, n, e){
55038         var x = Roo.lib.Event.getPageX(e);
55039         var r = Roo.lib.Dom.getRegion(n.firstChild);
55040         var px, pt, py = r.top + this.proxyOffsets[1];
55041         if((r.right - x) <= (r.right-r.left)/2){
55042             px = r.right+this.view.borderWidth;
55043             pt = "after";
55044         }else{
55045             px = r.left;
55046             pt = "before";
55047         }
55048         var oldIndex = this.view.getCellIndex(h);
55049         var newIndex = this.view.getCellIndex(n);
55050
55051         if(this.grid.colModel.isFixed(newIndex)){
55052             return false;
55053         }
55054
55055         var locked = this.grid.colModel.isLocked(newIndex);
55056
55057         if(pt == "after"){
55058             newIndex++;
55059         }
55060         if(oldIndex < newIndex){
55061             newIndex--;
55062         }
55063         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
55064             return false;
55065         }
55066         px +=  this.proxyOffsets[0];
55067         this.proxyTop.setLeftTop(px, py);
55068         this.proxyTop.show();
55069         if(!this.bottomOffset){
55070             this.bottomOffset = this.view.mainHd.getHeight();
55071         }
55072         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
55073         this.proxyBottom.show();
55074         return pt;
55075     },
55076
55077     onNodeEnter : function(n, dd, e, data){
55078         if(data.header != n){
55079             this.positionIndicator(data.header, n, e);
55080         }
55081     },
55082
55083     onNodeOver : function(n, dd, e, data){
55084         var result = false;
55085         if(data.header != n){
55086             result = this.positionIndicator(data.header, n, e);
55087         }
55088         if(!result){
55089             this.proxyTop.hide();
55090             this.proxyBottom.hide();
55091         }
55092         return result ? this.dropAllowed : this.dropNotAllowed;
55093     },
55094
55095     onNodeOut : function(n, dd, e, data){
55096         this.proxyTop.hide();
55097         this.proxyBottom.hide();
55098     },
55099
55100     onNodeDrop : function(n, dd, e, data){
55101         var h = data.header;
55102         if(h != n){
55103             var cm = this.grid.colModel;
55104             var x = Roo.lib.Event.getPageX(e);
55105             var r = Roo.lib.Dom.getRegion(n.firstChild);
55106             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
55107             var oldIndex = this.view.getCellIndex(h);
55108             var newIndex = this.view.getCellIndex(n);
55109             var locked = cm.isLocked(newIndex);
55110             if(pt == "after"){
55111                 newIndex++;
55112             }
55113             if(oldIndex < newIndex){
55114                 newIndex--;
55115             }
55116             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
55117                 return false;
55118             }
55119             cm.setLocked(oldIndex, locked, true);
55120             cm.moveColumn(oldIndex, newIndex);
55121             this.grid.fireEvent("columnmove", oldIndex, newIndex);
55122             return true;
55123         }
55124         return false;
55125     }
55126 });
55127 /*
55128  * Based on:
55129  * Ext JS Library 1.1.1
55130  * Copyright(c) 2006-2007, Ext JS, LLC.
55131  *
55132  * Originally Released Under LGPL - original licence link has changed is not relivant.
55133  *
55134  * Fork - LGPL
55135  * <script type="text/javascript">
55136  */
55137   
55138 /**
55139  * @class Roo.grid.GridView
55140  * @extends Roo.util.Observable
55141  *
55142  * @constructor
55143  * @param {Object} config
55144  */
55145 Roo.grid.GridView = function(config){
55146     Roo.grid.GridView.superclass.constructor.call(this);
55147     this.el = null;
55148
55149     Roo.apply(this, config);
55150 };
55151
55152 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
55153
55154     unselectable :  'unselectable="on"',
55155     unselectableCls :  'x-unselectable',
55156     
55157     
55158     rowClass : "x-grid-row",
55159
55160     cellClass : "x-grid-col",
55161
55162     tdClass : "x-grid-td",
55163
55164     hdClass : "x-grid-hd",
55165
55166     splitClass : "x-grid-split",
55167
55168     sortClasses : ["sort-asc", "sort-desc"],
55169
55170     enableMoveAnim : false,
55171
55172     hlColor: "C3DAF9",
55173
55174     dh : Roo.DomHelper,
55175
55176     fly : Roo.Element.fly,
55177
55178     css : Roo.util.CSS,
55179
55180     borderWidth: 1,
55181
55182     splitOffset: 3,
55183
55184     scrollIncrement : 22,
55185
55186     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
55187
55188     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
55189
55190     bind : function(ds, cm){
55191         if(this.ds){
55192             this.ds.un("load", this.onLoad, this);
55193             this.ds.un("datachanged", this.onDataChange, this);
55194             this.ds.un("add", this.onAdd, this);
55195             this.ds.un("remove", this.onRemove, this);
55196             this.ds.un("update", this.onUpdate, this);
55197             this.ds.un("clear", this.onClear, this);
55198         }
55199         if(ds){
55200             ds.on("load", this.onLoad, this);
55201             ds.on("datachanged", this.onDataChange, this);
55202             ds.on("add", this.onAdd, this);
55203             ds.on("remove", this.onRemove, this);
55204             ds.on("update", this.onUpdate, this);
55205             ds.on("clear", this.onClear, this);
55206         }
55207         this.ds = ds;
55208
55209         if(this.cm){
55210             this.cm.un("widthchange", this.onColWidthChange, this);
55211             this.cm.un("headerchange", this.onHeaderChange, this);
55212             this.cm.un("hiddenchange", this.onHiddenChange, this);
55213             this.cm.un("columnmoved", this.onColumnMove, this);
55214             this.cm.un("columnlockchange", this.onColumnLock, this);
55215         }
55216         if(cm){
55217             this.generateRules(cm);
55218             cm.on("widthchange", this.onColWidthChange, this);
55219             cm.on("headerchange", this.onHeaderChange, this);
55220             cm.on("hiddenchange", this.onHiddenChange, this);
55221             cm.on("columnmoved", this.onColumnMove, this);
55222             cm.on("columnlockchange", this.onColumnLock, this);
55223         }
55224         this.cm = cm;
55225     },
55226
55227     init: function(grid){
55228         Roo.grid.GridView.superclass.init.call(this, grid);
55229
55230         this.bind(grid.dataSource, grid.colModel);
55231
55232         grid.on("headerclick", this.handleHeaderClick, this);
55233
55234         if(grid.trackMouseOver){
55235             grid.on("mouseover", this.onRowOver, this);
55236             grid.on("mouseout", this.onRowOut, this);
55237         }
55238         grid.cancelTextSelection = function(){};
55239         this.gridId = grid.id;
55240
55241         var tpls = this.templates || {};
55242
55243         if(!tpls.master){
55244             tpls.master = new Roo.Template(
55245                '<div class="x-grid" hidefocus="true">',
55246                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
55247                   '<div class="x-grid-topbar"></div>',
55248                   '<div class="x-grid-scroller"><div></div></div>',
55249                   '<div class="x-grid-locked">',
55250                       '<div class="x-grid-header">{lockedHeader}</div>',
55251                       '<div class="x-grid-body">{lockedBody}</div>',
55252                   "</div>",
55253                   '<div class="x-grid-viewport">',
55254                       '<div class="x-grid-header">{header}</div>',
55255                       '<div class="x-grid-body">{body}</div>',
55256                   "</div>",
55257                   '<div class="x-grid-bottombar"></div>',
55258                  
55259                   '<div class="x-grid-resize-proxy">&#160;</div>',
55260                "</div>"
55261             );
55262             tpls.master.disableformats = true;
55263         }
55264
55265         if(!tpls.header){
55266             tpls.header = new Roo.Template(
55267                '<table border="0" cellspacing="0" cellpadding="0">',
55268                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
55269                "</table>{splits}"
55270             );
55271             tpls.header.disableformats = true;
55272         }
55273         tpls.header.compile();
55274
55275         if(!tpls.hcell){
55276             tpls.hcell = new Roo.Template(
55277                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
55278                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
55279                 "</div></td>"
55280              );
55281              tpls.hcell.disableFormats = true;
55282         }
55283         tpls.hcell.compile();
55284
55285         if(!tpls.hsplit){
55286             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
55287                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
55288             tpls.hsplit.disableFormats = true;
55289         }
55290         tpls.hsplit.compile();
55291
55292         if(!tpls.body){
55293             tpls.body = new Roo.Template(
55294                '<table border="0" cellspacing="0" cellpadding="0">',
55295                "<tbody>{rows}</tbody>",
55296                "</table>"
55297             );
55298             tpls.body.disableFormats = true;
55299         }
55300         tpls.body.compile();
55301
55302         if(!tpls.row){
55303             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
55304             tpls.row.disableFormats = true;
55305         }
55306         tpls.row.compile();
55307
55308         if(!tpls.cell){
55309             tpls.cell = new Roo.Template(
55310                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
55311                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
55312                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
55313                 "</td>"
55314             );
55315             tpls.cell.disableFormats = true;
55316         }
55317         tpls.cell.compile();
55318
55319         this.templates = tpls;
55320     },
55321
55322     // remap these for backwards compat
55323     onColWidthChange : function(){
55324         this.updateColumns.apply(this, arguments);
55325     },
55326     onHeaderChange : function(){
55327         this.updateHeaders.apply(this, arguments);
55328     }, 
55329     onHiddenChange : function(){
55330         this.handleHiddenChange.apply(this, arguments);
55331     },
55332     onColumnMove : function(){
55333         this.handleColumnMove.apply(this, arguments);
55334     },
55335     onColumnLock : function(){
55336         this.handleLockChange.apply(this, arguments);
55337     },
55338
55339     onDataChange : function(){
55340         this.refresh();
55341         this.updateHeaderSortState();
55342     },
55343
55344     onClear : function(){
55345         this.refresh();
55346     },
55347
55348     onUpdate : function(ds, record){
55349         this.refreshRow(record);
55350     },
55351
55352     refreshRow : function(record){
55353         var ds = this.ds, index;
55354         if(typeof record == 'number'){
55355             index = record;
55356             record = ds.getAt(index);
55357         }else{
55358             index = ds.indexOf(record);
55359         }
55360         this.insertRows(ds, index, index, true);
55361         this.onRemove(ds, record, index+1, true);
55362         this.syncRowHeights(index, index);
55363         this.layout();
55364         this.fireEvent("rowupdated", this, index, record);
55365     },
55366
55367     onAdd : function(ds, records, index){
55368         this.insertRows(ds, index, index + (records.length-1));
55369     },
55370
55371     onRemove : function(ds, record, index, isUpdate){
55372         if(isUpdate !== true){
55373             this.fireEvent("beforerowremoved", this, index, record);
55374         }
55375         var bt = this.getBodyTable(), lt = this.getLockedTable();
55376         if(bt.rows[index]){
55377             bt.firstChild.removeChild(bt.rows[index]);
55378         }
55379         if(lt.rows[index]){
55380             lt.firstChild.removeChild(lt.rows[index]);
55381         }
55382         if(isUpdate !== true){
55383             this.stripeRows(index);
55384             this.syncRowHeights(index, index);
55385             this.layout();
55386             this.fireEvent("rowremoved", this, index, record);
55387         }
55388     },
55389
55390     onLoad : function(){
55391         this.scrollToTop();
55392     },
55393
55394     /**
55395      * Scrolls the grid to the top
55396      */
55397     scrollToTop : function(){
55398         if(this.scroller){
55399             this.scroller.dom.scrollTop = 0;
55400             this.syncScroll();
55401         }
55402     },
55403
55404     /**
55405      * Gets a panel in the header of the grid that can be used for toolbars etc.
55406      * After modifying the contents of this panel a call to grid.autoSize() may be
55407      * required to register any changes in size.
55408      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
55409      * @return Roo.Element
55410      */
55411     getHeaderPanel : function(doShow){
55412         if(doShow){
55413             this.headerPanel.show();
55414         }
55415         return this.headerPanel;
55416     },
55417
55418     /**
55419      * Gets a panel in the footer of the grid that can be used for toolbars etc.
55420      * After modifying the contents of this panel a call to grid.autoSize() may be
55421      * required to register any changes in size.
55422      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
55423      * @return Roo.Element
55424      */
55425     getFooterPanel : function(doShow){
55426         if(doShow){
55427             this.footerPanel.show();
55428         }
55429         return this.footerPanel;
55430     },
55431
55432     initElements : function(){
55433         var E = Roo.Element;
55434         var el = this.grid.getGridEl().dom.firstChild;
55435         var cs = el.childNodes;
55436
55437         this.el = new E(el);
55438         
55439          this.focusEl = new E(el.firstChild);
55440         this.focusEl.swallowEvent("click", true);
55441         
55442         this.headerPanel = new E(cs[1]);
55443         this.headerPanel.enableDisplayMode("block");
55444
55445         this.scroller = new E(cs[2]);
55446         this.scrollSizer = new E(this.scroller.dom.firstChild);
55447
55448         this.lockedWrap = new E(cs[3]);
55449         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
55450         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
55451
55452         this.mainWrap = new E(cs[4]);
55453         this.mainHd = new E(this.mainWrap.dom.firstChild);
55454         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
55455
55456         this.footerPanel = new E(cs[5]);
55457         this.footerPanel.enableDisplayMode("block");
55458
55459         this.resizeProxy = new E(cs[6]);
55460
55461         this.headerSelector = String.format(
55462            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
55463            this.lockedHd.id, this.mainHd.id
55464         );
55465
55466         this.splitterSelector = String.format(
55467            '#{0} div.x-grid-split, #{1} div.x-grid-split',
55468            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
55469         );
55470     },
55471     idToCssName : function(s)
55472     {
55473         return s.replace(/[^a-z0-9]+/ig, '-');
55474     },
55475
55476     getHeaderCell : function(index){
55477         return Roo.DomQuery.select(this.headerSelector)[index];
55478     },
55479
55480     getHeaderCellMeasure : function(index){
55481         return this.getHeaderCell(index).firstChild;
55482     },
55483
55484     getHeaderCellText : function(index){
55485         return this.getHeaderCell(index).firstChild.firstChild;
55486     },
55487
55488     getLockedTable : function(){
55489         return this.lockedBody.dom.firstChild;
55490     },
55491
55492     getBodyTable : function(){
55493         return this.mainBody.dom.firstChild;
55494     },
55495
55496     getLockedRow : function(index){
55497         return this.getLockedTable().rows[index];
55498     },
55499
55500     getRow : function(index){
55501         return this.getBodyTable().rows[index];
55502     },
55503
55504     getRowComposite : function(index){
55505         if(!this.rowEl){
55506             this.rowEl = new Roo.CompositeElementLite();
55507         }
55508         var els = [], lrow, mrow;
55509         if(lrow = this.getLockedRow(index)){
55510             els.push(lrow);
55511         }
55512         if(mrow = this.getRow(index)){
55513             els.push(mrow);
55514         }
55515         this.rowEl.elements = els;
55516         return this.rowEl;
55517     },
55518     /**
55519      * Gets the 'td' of the cell
55520      * 
55521      * @param {Integer} rowIndex row to select
55522      * @param {Integer} colIndex column to select
55523      * 
55524      * @return {Object} 
55525      */
55526     getCell : function(rowIndex, colIndex){
55527         var locked = this.cm.getLockedCount();
55528         var source;
55529         if(colIndex < locked){
55530             source = this.lockedBody.dom.firstChild;
55531         }else{
55532             source = this.mainBody.dom.firstChild;
55533             colIndex -= locked;
55534         }
55535         return source.rows[rowIndex].childNodes[colIndex];
55536     },
55537
55538     getCellText : function(rowIndex, colIndex){
55539         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
55540     },
55541
55542     getCellBox : function(cell){
55543         var b = this.fly(cell).getBox();
55544         if(Roo.isOpera){ // opera fails to report the Y
55545             b.y = cell.offsetTop + this.mainBody.getY();
55546         }
55547         return b;
55548     },
55549
55550     getCellIndex : function(cell){
55551         var id = String(cell.className).match(this.cellRE);
55552         if(id){
55553             return parseInt(id[1], 10);
55554         }
55555         return 0;
55556     },
55557
55558     findHeaderIndex : function(n){
55559         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55560         return r ? this.getCellIndex(r) : false;
55561     },
55562
55563     findHeaderCell : function(n){
55564         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55565         return r ? r : false;
55566     },
55567
55568     findRowIndex : function(n){
55569         if(!n){
55570             return false;
55571         }
55572         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
55573         return r ? r.rowIndex : false;
55574     },
55575
55576     findCellIndex : function(node){
55577         var stop = this.el.dom;
55578         while(node && node != stop){
55579             if(this.findRE.test(node.className)){
55580                 return this.getCellIndex(node);
55581             }
55582             node = node.parentNode;
55583         }
55584         return false;
55585     },
55586
55587     getColumnId : function(index){
55588         return this.cm.getColumnId(index);
55589     },
55590
55591     getSplitters : function()
55592     {
55593         if(this.splitterSelector){
55594            return Roo.DomQuery.select(this.splitterSelector);
55595         }else{
55596             return null;
55597       }
55598     },
55599
55600     getSplitter : function(index){
55601         return this.getSplitters()[index];
55602     },
55603
55604     onRowOver : function(e, t){
55605         var row;
55606         if((row = this.findRowIndex(t)) !== false){
55607             this.getRowComposite(row).addClass("x-grid-row-over");
55608         }
55609     },
55610
55611     onRowOut : function(e, t){
55612         var row;
55613         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
55614             this.getRowComposite(row).removeClass("x-grid-row-over");
55615         }
55616     },
55617
55618     renderHeaders : function(){
55619         var cm = this.cm;
55620         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
55621         var cb = [], lb = [], sb = [], lsb = [], p = {};
55622         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55623             p.cellId = "x-grid-hd-0-" + i;
55624             p.splitId = "x-grid-csplit-0-" + i;
55625             p.id = cm.getColumnId(i);
55626             p.value = cm.getColumnHeader(i) || "";
55627             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
55628             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
55629             if(!cm.isLocked(i)){
55630                 cb[cb.length] = ct.apply(p);
55631                 sb[sb.length] = st.apply(p);
55632             }else{
55633                 lb[lb.length] = ct.apply(p);
55634                 lsb[lsb.length] = st.apply(p);
55635             }
55636         }
55637         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
55638                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
55639     },
55640
55641     updateHeaders : function(){
55642         var html = this.renderHeaders();
55643         this.lockedHd.update(html[0]);
55644         this.mainHd.update(html[1]);
55645     },
55646
55647     /**
55648      * Focuses the specified row.
55649      * @param {Number} row The row index
55650      */
55651     focusRow : function(row)
55652     {
55653         //Roo.log('GridView.focusRow');
55654         var x = this.scroller.dom.scrollLeft;
55655         this.focusCell(row, 0, false);
55656         this.scroller.dom.scrollLeft = x;
55657     },
55658
55659     /**
55660      * Focuses the specified cell.
55661      * @param {Number} row The row index
55662      * @param {Number} col The column index
55663      * @param {Boolean} hscroll false to disable horizontal scrolling
55664      */
55665     focusCell : function(row, col, hscroll)
55666     {
55667         //Roo.log('GridView.focusCell');
55668         var el = this.ensureVisible(row, col, hscroll);
55669         this.focusEl.alignTo(el, "tl-tl");
55670         if(Roo.isGecko){
55671             this.focusEl.focus();
55672         }else{
55673             this.focusEl.focus.defer(1, this.focusEl);
55674         }
55675     },
55676
55677     /**
55678      * Scrolls the specified cell into view
55679      * @param {Number} row The row index
55680      * @param {Number} col The column index
55681      * @param {Boolean} hscroll false to disable horizontal scrolling
55682      */
55683     ensureVisible : function(row, col, hscroll)
55684     {
55685         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
55686         //return null; //disable for testing.
55687         if(typeof row != "number"){
55688             row = row.rowIndex;
55689         }
55690         if(row < 0 && row >= this.ds.getCount()){
55691             return  null;
55692         }
55693         col = (col !== undefined ? col : 0);
55694         var cm = this.grid.colModel;
55695         while(cm.isHidden(col)){
55696             col++;
55697         }
55698
55699         var el = this.getCell(row, col);
55700         if(!el){
55701             return null;
55702         }
55703         var c = this.scroller.dom;
55704
55705         var ctop = parseInt(el.offsetTop, 10);
55706         var cleft = parseInt(el.offsetLeft, 10);
55707         var cbot = ctop + el.offsetHeight;
55708         var cright = cleft + el.offsetWidth;
55709         
55710         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
55711         var stop = parseInt(c.scrollTop, 10);
55712         var sleft = parseInt(c.scrollLeft, 10);
55713         var sbot = stop + ch;
55714         var sright = sleft + c.clientWidth;
55715         /*
55716         Roo.log('GridView.ensureVisible:' +
55717                 ' ctop:' + ctop +
55718                 ' c.clientHeight:' + c.clientHeight +
55719                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
55720                 ' stop:' + stop +
55721                 ' cbot:' + cbot +
55722                 ' sbot:' + sbot +
55723                 ' ch:' + ch  
55724                 );
55725         */
55726         if(ctop < stop){
55727              c.scrollTop = ctop;
55728             //Roo.log("set scrolltop to ctop DISABLE?");
55729         }else if(cbot > sbot){
55730             //Roo.log("set scrolltop to cbot-ch");
55731             c.scrollTop = cbot-ch;
55732         }
55733         
55734         if(hscroll !== false){
55735             if(cleft < sleft){
55736                 c.scrollLeft = cleft;
55737             }else if(cright > sright){
55738                 c.scrollLeft = cright-c.clientWidth;
55739             }
55740         }
55741          
55742         return el;
55743     },
55744
55745     updateColumns : function(){
55746         this.grid.stopEditing();
55747         var cm = this.grid.colModel, colIds = this.getColumnIds();
55748         //var totalWidth = cm.getTotalWidth();
55749         var pos = 0;
55750         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55751             //if(cm.isHidden(i)) continue;
55752             var w = cm.getColumnWidth(i);
55753             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55754             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55755         }
55756         this.updateSplitters();
55757     },
55758
55759     generateRules : function(cm){
55760         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
55761         Roo.util.CSS.removeStyleSheet(rulesId);
55762         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55763             var cid = cm.getColumnId(i);
55764             var align = '';
55765             if(cm.config[i].align){
55766                 align = 'text-align:'+cm.config[i].align+';';
55767             }
55768             var hidden = '';
55769             if(cm.isHidden(i)){
55770                 hidden = 'display:none;';
55771             }
55772             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
55773             ruleBuf.push(
55774                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
55775                     this.hdSelector, cid, " {\n", align, width, "}\n",
55776                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
55777                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
55778         }
55779         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55780     },
55781
55782     updateSplitters : function(){
55783         var cm = this.cm, s = this.getSplitters();
55784         if(s){ // splitters not created yet
55785             var pos = 0, locked = true;
55786             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55787                 if(cm.isHidden(i)) {
55788                     continue;
55789                 }
55790                 var w = cm.getColumnWidth(i); // make sure it's a number
55791                 if(!cm.isLocked(i) && locked){
55792                     pos = 0;
55793                     locked = false;
55794                 }
55795                 pos += w;
55796                 s[i].style.left = (pos-this.splitOffset) + "px";
55797             }
55798         }
55799     },
55800
55801     handleHiddenChange : function(colModel, colIndex, hidden){
55802         if(hidden){
55803             this.hideColumn(colIndex);
55804         }else{
55805             this.unhideColumn(colIndex);
55806         }
55807     },
55808
55809     hideColumn : function(colIndex){
55810         var cid = this.getColumnId(colIndex);
55811         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
55812         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
55813         if(Roo.isSafari){
55814             this.updateHeaders();
55815         }
55816         this.updateSplitters();
55817         this.layout();
55818     },
55819
55820     unhideColumn : function(colIndex){
55821         var cid = this.getColumnId(colIndex);
55822         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
55823         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
55824
55825         if(Roo.isSafari){
55826             this.updateHeaders();
55827         }
55828         this.updateSplitters();
55829         this.layout();
55830     },
55831
55832     insertRows : function(dm, firstRow, lastRow, isUpdate){
55833         if(firstRow == 0 && lastRow == dm.getCount()-1){
55834             this.refresh();
55835         }else{
55836             if(!isUpdate){
55837                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
55838             }
55839             var s = this.getScrollState();
55840             var markup = this.renderRows(firstRow, lastRow);
55841             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
55842             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
55843             this.restoreScroll(s);
55844             if(!isUpdate){
55845                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
55846                 this.syncRowHeights(firstRow, lastRow);
55847                 this.stripeRows(firstRow);
55848                 this.layout();
55849             }
55850         }
55851     },
55852
55853     bufferRows : function(markup, target, index){
55854         var before = null, trows = target.rows, tbody = target.tBodies[0];
55855         if(index < trows.length){
55856             before = trows[index];
55857         }
55858         var b = document.createElement("div");
55859         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
55860         var rows = b.firstChild.rows;
55861         for(var i = 0, len = rows.length; i < len; i++){
55862             if(before){
55863                 tbody.insertBefore(rows[0], before);
55864             }else{
55865                 tbody.appendChild(rows[0]);
55866             }
55867         }
55868         b.innerHTML = "";
55869         b = null;
55870     },
55871
55872     deleteRows : function(dm, firstRow, lastRow){
55873         if(dm.getRowCount()<1){
55874             this.fireEvent("beforerefresh", this);
55875             this.mainBody.update("");
55876             this.lockedBody.update("");
55877             this.fireEvent("refresh", this);
55878         }else{
55879             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
55880             var bt = this.getBodyTable();
55881             var tbody = bt.firstChild;
55882             var rows = bt.rows;
55883             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
55884                 tbody.removeChild(rows[firstRow]);
55885             }
55886             this.stripeRows(firstRow);
55887             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
55888         }
55889     },
55890
55891     updateRows : function(dataSource, firstRow, lastRow){
55892         var s = this.getScrollState();
55893         this.refresh();
55894         this.restoreScroll(s);
55895     },
55896
55897     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
55898         if(!noRefresh){
55899            this.refresh();
55900         }
55901         this.updateHeaderSortState();
55902     },
55903
55904     getScrollState : function(){
55905         
55906         var sb = this.scroller.dom;
55907         return {left: sb.scrollLeft, top: sb.scrollTop};
55908     },
55909
55910     stripeRows : function(startRow){
55911         if(!this.grid.stripeRows || this.ds.getCount() < 1){
55912             return;
55913         }
55914         startRow = startRow || 0;
55915         var rows = this.getBodyTable().rows;
55916         var lrows = this.getLockedTable().rows;
55917         var cls = ' x-grid-row-alt ';
55918         for(var i = startRow, len = rows.length; i < len; i++){
55919             var row = rows[i], lrow = lrows[i];
55920             var isAlt = ((i+1) % 2 == 0);
55921             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
55922             if(isAlt == hasAlt){
55923                 continue;
55924             }
55925             if(isAlt){
55926                 row.className += " x-grid-row-alt";
55927             }else{
55928                 row.className = row.className.replace("x-grid-row-alt", "");
55929             }
55930             if(lrow){
55931                 lrow.className = row.className;
55932             }
55933         }
55934     },
55935
55936     restoreScroll : function(state){
55937         //Roo.log('GridView.restoreScroll');
55938         var sb = this.scroller.dom;
55939         sb.scrollLeft = state.left;
55940         sb.scrollTop = state.top;
55941         this.syncScroll();
55942     },
55943
55944     syncScroll : function(){
55945         //Roo.log('GridView.syncScroll');
55946         var sb = this.scroller.dom;
55947         var sh = this.mainHd.dom;
55948         var bs = this.mainBody.dom;
55949         var lv = this.lockedBody.dom;
55950         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
55951         lv.scrollTop = bs.scrollTop = sb.scrollTop;
55952     },
55953
55954     handleScroll : function(e){
55955         this.syncScroll();
55956         var sb = this.scroller.dom;
55957         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
55958         e.stopEvent();
55959     },
55960
55961     handleWheel : function(e){
55962         var d = e.getWheelDelta();
55963         this.scroller.dom.scrollTop -= d*22;
55964         // set this here to prevent jumpy scrolling on large tables
55965         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
55966         e.stopEvent();
55967     },
55968
55969     renderRows : function(startRow, endRow){
55970         // pull in all the crap needed to render rows
55971         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
55972         var colCount = cm.getColumnCount();
55973
55974         if(ds.getCount() < 1){
55975             return ["", ""];
55976         }
55977
55978         // build a map for all the columns
55979         var cs = [];
55980         for(var i = 0; i < colCount; i++){
55981             var name = cm.getDataIndex(i);
55982             cs[i] = {
55983                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
55984                 renderer : cm.getRenderer(i),
55985                 id : cm.getColumnId(i),
55986                 locked : cm.isLocked(i),
55987                 has_editor : cm.isCellEditable(i)
55988             };
55989         }
55990
55991         startRow = startRow || 0;
55992         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
55993
55994         // records to render
55995         var rs = ds.getRange(startRow, endRow);
55996
55997         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
55998     },
55999
56000     // As much as I hate to duplicate code, this was branched because FireFox really hates
56001     // [].join("") on strings. The performance difference was substantial enough to
56002     // branch this function
56003     doRender : Roo.isGecko ?
56004             function(cs, rs, ds, startRow, colCount, stripe){
56005                 var ts = this.templates, ct = ts.cell, rt = ts.row;
56006                 // buffers
56007                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
56008                 
56009                 var hasListener = this.grid.hasListener('rowclass');
56010                 var rowcfg = {};
56011                 for(var j = 0, len = rs.length; j < len; j++){
56012                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
56013                     for(var i = 0; i < colCount; i++){
56014                         c = cs[i];
56015                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
56016                         p.id = c.id;
56017                         p.css = p.attr = "";
56018                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
56019                         if(p.value == undefined || p.value === "") {
56020                             p.value = "&#160;";
56021                         }
56022                         if(c.has_editor){
56023                             p.css += ' x-grid-editable-cell';
56024                         }
56025                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
56026                             p.css +=  ' x-grid-dirty-cell';
56027                         }
56028                         var markup = ct.apply(p);
56029                         if(!c.locked){
56030                             cb+= markup;
56031                         }else{
56032                             lcb+= markup;
56033                         }
56034                     }
56035                     var alt = [];
56036                     if(stripe && ((rowIndex+1) % 2 == 0)){
56037                         alt.push("x-grid-row-alt")
56038                     }
56039                     if(r.dirty){
56040                         alt.push(  " x-grid-dirty-row");
56041                     }
56042                     rp.cells = lcb;
56043                     if(this.getRowClass){
56044                         alt.push(this.getRowClass(r, rowIndex));
56045                     }
56046                     if (hasListener) {
56047                         rowcfg = {
56048                              
56049                             record: r,
56050                             rowIndex : rowIndex,
56051                             rowClass : ''
56052                         };
56053                         this.grid.fireEvent('rowclass', this, rowcfg);
56054                         alt.push(rowcfg.rowClass);
56055                     }
56056                     rp.alt = alt.join(" ");
56057                     lbuf+= rt.apply(rp);
56058                     rp.cells = cb;
56059                     buf+=  rt.apply(rp);
56060                 }
56061                 return [lbuf, buf];
56062             } :
56063             function(cs, rs, ds, startRow, colCount, stripe){
56064                 var ts = this.templates, ct = ts.cell, rt = ts.row;
56065                 // buffers
56066                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
56067                 var hasListener = this.grid.hasListener('rowclass');
56068  
56069                 var rowcfg = {};
56070                 for(var j = 0, len = rs.length; j < len; j++){
56071                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
56072                     for(var i = 0; i < colCount; i++){
56073                         c = cs[i];
56074                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
56075                         p.id = c.id;
56076                         p.css = p.attr = "";
56077                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
56078                         if(p.value == undefined || p.value === "") {
56079                             p.value = "&#160;";
56080                         }
56081                         //Roo.log(c);
56082                          if(c.has_editor){
56083                             p.css += ' x-grid-editable-cell';
56084                         }
56085                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
56086                             p.css += ' x-grid-dirty-cell' 
56087                         }
56088                         
56089                         var markup = ct.apply(p);
56090                         if(!c.locked){
56091                             cb[cb.length] = markup;
56092                         }else{
56093                             lcb[lcb.length] = markup;
56094                         }
56095                     }
56096                     var alt = [];
56097                     if(stripe && ((rowIndex+1) % 2 == 0)){
56098                         alt.push( "x-grid-row-alt");
56099                     }
56100                     if(r.dirty){
56101                         alt.push(" x-grid-dirty-row");
56102                     }
56103                     rp.cells = lcb;
56104                     if(this.getRowClass){
56105                         alt.push( this.getRowClass(r, rowIndex));
56106                     }
56107                     if (hasListener) {
56108                         rowcfg = {
56109                              
56110                             record: r,
56111                             rowIndex : rowIndex,
56112                             rowClass : ''
56113                         };
56114                         this.grid.fireEvent('rowclass', this, rowcfg);
56115                         alt.push(rowcfg.rowClass);
56116                     }
56117                     
56118                     rp.alt = alt.join(" ");
56119                     rp.cells = lcb.join("");
56120                     lbuf[lbuf.length] = rt.apply(rp);
56121                     rp.cells = cb.join("");
56122                     buf[buf.length] =  rt.apply(rp);
56123                 }
56124                 return [lbuf.join(""), buf.join("")];
56125             },
56126
56127     renderBody : function(){
56128         var markup = this.renderRows();
56129         var bt = this.templates.body;
56130         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
56131     },
56132
56133     /**
56134      * Refreshes the grid
56135      * @param {Boolean} headersToo
56136      */
56137     refresh : function(headersToo){
56138         this.fireEvent("beforerefresh", this);
56139         this.grid.stopEditing();
56140         var result = this.renderBody();
56141         this.lockedBody.update(result[0]);
56142         this.mainBody.update(result[1]);
56143         if(headersToo === true){
56144             this.updateHeaders();
56145             this.updateColumns();
56146             this.updateSplitters();
56147             this.updateHeaderSortState();
56148         }
56149         this.syncRowHeights();
56150         this.layout();
56151         this.fireEvent("refresh", this);
56152     },
56153
56154     handleColumnMove : function(cm, oldIndex, newIndex){
56155         this.indexMap = null;
56156         var s = this.getScrollState();
56157         this.refresh(true);
56158         this.restoreScroll(s);
56159         this.afterMove(newIndex);
56160     },
56161
56162     afterMove : function(colIndex){
56163         if(this.enableMoveAnim && Roo.enableFx){
56164             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
56165         }
56166         // if multisort - fix sortOrder, and reload..
56167         if (this.grid.dataSource.multiSort) {
56168             // the we can call sort again..
56169             var dm = this.grid.dataSource;
56170             var cm = this.grid.colModel;
56171             var so = [];
56172             for(var i = 0; i < cm.config.length; i++ ) {
56173                 
56174                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
56175                     continue; // dont' bother, it's not in sort list or being set.
56176                 }
56177                 
56178                 so.push(cm.config[i].dataIndex);
56179             };
56180             dm.sortOrder = so;
56181             dm.load(dm.lastOptions);
56182             
56183             
56184         }
56185         
56186     },
56187
56188     updateCell : function(dm, rowIndex, dataIndex){
56189         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
56190         if(typeof colIndex == "undefined"){ // not present in grid
56191             return;
56192         }
56193         var cm = this.grid.colModel;
56194         var cell = this.getCell(rowIndex, colIndex);
56195         var cellText = this.getCellText(rowIndex, colIndex);
56196
56197         var p = {
56198             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
56199             id : cm.getColumnId(colIndex),
56200             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
56201         };
56202         var renderer = cm.getRenderer(colIndex);
56203         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
56204         if(typeof val == "undefined" || val === "") {
56205             val = "&#160;";
56206         }
56207         cellText.innerHTML = val;
56208         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
56209         this.syncRowHeights(rowIndex, rowIndex);
56210     },
56211
56212     calcColumnWidth : function(colIndex, maxRowsToMeasure){
56213         var maxWidth = 0;
56214         if(this.grid.autoSizeHeaders){
56215             var h = this.getHeaderCellMeasure(colIndex);
56216             maxWidth = Math.max(maxWidth, h.scrollWidth);
56217         }
56218         var tb, index;
56219         if(this.cm.isLocked(colIndex)){
56220             tb = this.getLockedTable();
56221             index = colIndex;
56222         }else{
56223             tb = this.getBodyTable();
56224             index = colIndex - this.cm.getLockedCount();
56225         }
56226         if(tb && tb.rows){
56227             var rows = tb.rows;
56228             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
56229             for(var i = 0; i < stopIndex; i++){
56230                 var cell = rows[i].childNodes[index].firstChild;
56231                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
56232             }
56233         }
56234         return maxWidth + /*margin for error in IE*/ 5;
56235     },
56236     /**
56237      * Autofit a column to its content.
56238      * @param {Number} colIndex
56239      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
56240      */
56241      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
56242          if(this.cm.isHidden(colIndex)){
56243              return; // can't calc a hidden column
56244          }
56245         if(forceMinSize){
56246             var cid = this.cm.getColumnId(colIndex);
56247             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
56248            if(this.grid.autoSizeHeaders){
56249                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
56250            }
56251         }
56252         var newWidth = this.calcColumnWidth(colIndex);
56253         this.cm.setColumnWidth(colIndex,
56254             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
56255         if(!suppressEvent){
56256             this.grid.fireEvent("columnresize", colIndex, newWidth);
56257         }
56258     },
56259
56260     /**
56261      * Autofits all columns to their content and then expands to fit any extra space in the grid
56262      */
56263      autoSizeColumns : function(){
56264         var cm = this.grid.colModel;
56265         var colCount = cm.getColumnCount();
56266         for(var i = 0; i < colCount; i++){
56267             this.autoSizeColumn(i, true, true);
56268         }
56269         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
56270             this.fitColumns();
56271         }else{
56272             this.updateColumns();
56273             this.layout();
56274         }
56275     },
56276
56277     /**
56278      * Autofits all columns to the grid's width proportionate with their current size
56279      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
56280      */
56281     fitColumns : function(reserveScrollSpace){
56282         var cm = this.grid.colModel;
56283         var colCount = cm.getColumnCount();
56284         var cols = [];
56285         var width = 0;
56286         var i, w;
56287         for (i = 0; i < colCount; i++){
56288             if(!cm.isHidden(i) && !cm.isFixed(i)){
56289                 w = cm.getColumnWidth(i);
56290                 cols.push(i);
56291                 cols.push(w);
56292                 width += w;
56293             }
56294         }
56295         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
56296         if(reserveScrollSpace){
56297             avail -= 17;
56298         }
56299         var frac = (avail - cm.getTotalWidth())/width;
56300         while (cols.length){
56301             w = cols.pop();
56302             i = cols.pop();
56303             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
56304         }
56305         this.updateColumns();
56306         this.layout();
56307     },
56308
56309     onRowSelect : function(rowIndex){
56310         var row = this.getRowComposite(rowIndex);
56311         row.addClass("x-grid-row-selected");
56312     },
56313
56314     onRowDeselect : function(rowIndex){
56315         var row = this.getRowComposite(rowIndex);
56316         row.removeClass("x-grid-row-selected");
56317     },
56318
56319     onCellSelect : function(row, col){
56320         var cell = this.getCell(row, col);
56321         if(cell){
56322             Roo.fly(cell).addClass("x-grid-cell-selected");
56323         }
56324     },
56325
56326     onCellDeselect : function(row, col){
56327         var cell = this.getCell(row, col);
56328         if(cell){
56329             Roo.fly(cell).removeClass("x-grid-cell-selected");
56330         }
56331     },
56332
56333     updateHeaderSortState : function(){
56334         
56335         // sort state can be single { field: xxx, direction : yyy}
56336         // or   { xxx=>ASC , yyy : DESC ..... }
56337         
56338         var mstate = {};
56339         if (!this.ds.multiSort) { 
56340             var state = this.ds.getSortState();
56341             if(!state){
56342                 return;
56343             }
56344             mstate[state.field] = state.direction;
56345             // FIXME... - this is not used here.. but might be elsewhere..
56346             this.sortState = state;
56347             
56348         } else {
56349             mstate = this.ds.sortToggle;
56350         }
56351         //remove existing sort classes..
56352         
56353         var sc = this.sortClasses;
56354         var hds = this.el.select(this.headerSelector).removeClass(sc);
56355         
56356         for(var f in mstate) {
56357         
56358             var sortColumn = this.cm.findColumnIndex(f);
56359             
56360             if(sortColumn != -1){
56361                 var sortDir = mstate[f];        
56362                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
56363             }
56364         }
56365         
56366          
56367         
56368     },
56369
56370
56371     handleHeaderClick : function(g, index,e){
56372         
56373         Roo.log("header click");
56374         
56375         if (Roo.isTouch) {
56376             // touch events on header are handled by context
56377             this.handleHdCtx(g,index,e);
56378             return;
56379         }
56380         
56381         
56382         if(this.headersDisabled){
56383             return;
56384         }
56385         var dm = g.dataSource, cm = g.colModel;
56386         if(!cm.isSortable(index)){
56387             return;
56388         }
56389         g.stopEditing();
56390         
56391         if (dm.multiSort) {
56392             // update the sortOrder
56393             var so = [];
56394             for(var i = 0; i < cm.config.length; i++ ) {
56395                 
56396                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
56397                     continue; // dont' bother, it's not in sort list or being set.
56398                 }
56399                 
56400                 so.push(cm.config[i].dataIndex);
56401             };
56402             dm.sortOrder = so;
56403         }
56404         
56405         
56406         dm.sort(cm.getDataIndex(index));
56407     },
56408
56409
56410     destroy : function(){
56411         if(this.colMenu){
56412             this.colMenu.removeAll();
56413             Roo.menu.MenuMgr.unregister(this.colMenu);
56414             this.colMenu.getEl().remove();
56415             delete this.colMenu;
56416         }
56417         if(this.hmenu){
56418             this.hmenu.removeAll();
56419             Roo.menu.MenuMgr.unregister(this.hmenu);
56420             this.hmenu.getEl().remove();
56421             delete this.hmenu;
56422         }
56423         if(this.grid.enableColumnMove){
56424             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56425             if(dds){
56426                 for(var dd in dds){
56427                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
56428                         var elid = dds[dd].dragElId;
56429                         dds[dd].unreg();
56430                         Roo.get(elid).remove();
56431                     } else if(dds[dd].config.isTarget){
56432                         dds[dd].proxyTop.remove();
56433                         dds[dd].proxyBottom.remove();
56434                         dds[dd].unreg();
56435                     }
56436                     if(Roo.dd.DDM.locationCache[dd]){
56437                         delete Roo.dd.DDM.locationCache[dd];
56438                     }
56439                 }
56440                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56441             }
56442         }
56443         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
56444         this.bind(null, null);
56445         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
56446     },
56447
56448     handleLockChange : function(){
56449         this.refresh(true);
56450     },
56451
56452     onDenyColumnLock : function(){
56453
56454     },
56455
56456     onDenyColumnHide : function(){
56457
56458     },
56459
56460     handleHdMenuClick : function(item){
56461         var index = this.hdCtxIndex;
56462         var cm = this.cm, ds = this.ds;
56463         switch(item.id){
56464             case "asc":
56465                 ds.sort(cm.getDataIndex(index), "ASC");
56466                 break;
56467             case "desc":
56468                 ds.sort(cm.getDataIndex(index), "DESC");
56469                 break;
56470             case "lock":
56471                 var lc = cm.getLockedCount();
56472                 if(cm.getColumnCount(true) <= lc+1){
56473                     this.onDenyColumnLock();
56474                     return;
56475                 }
56476                 if(lc != index){
56477                     cm.setLocked(index, true, true);
56478                     cm.moveColumn(index, lc);
56479                     this.grid.fireEvent("columnmove", index, lc);
56480                 }else{
56481                     cm.setLocked(index, true);
56482                 }
56483             break;
56484             case "unlock":
56485                 var lc = cm.getLockedCount();
56486                 if((lc-1) != index){
56487                     cm.setLocked(index, false, true);
56488                     cm.moveColumn(index, lc-1);
56489                     this.grid.fireEvent("columnmove", index, lc-1);
56490                 }else{
56491                     cm.setLocked(index, false);
56492                 }
56493             break;
56494             case 'wider': // used to expand cols on touch..
56495             case 'narrow':
56496                 var cw = cm.getColumnWidth(index);
56497                 cw += (item.id == 'wider' ? 1 : -1) * 50;
56498                 cw = Math.max(0, cw);
56499                 cw = Math.min(cw,4000);
56500                 cm.setColumnWidth(index, cw);
56501                 break;
56502                 
56503             default:
56504                 index = cm.getIndexById(item.id.substr(4));
56505                 if(index != -1){
56506                     if(item.checked && cm.getColumnCount(true) <= 1){
56507                         this.onDenyColumnHide();
56508                         return false;
56509                     }
56510                     cm.setHidden(index, item.checked);
56511                 }
56512         }
56513         return true;
56514     },
56515
56516     beforeColMenuShow : function(){
56517         var cm = this.cm,  colCount = cm.getColumnCount();
56518         this.colMenu.removeAll();
56519         for(var i = 0; i < colCount; i++){
56520             this.colMenu.add(new Roo.menu.CheckItem({
56521                 id: "col-"+cm.getColumnId(i),
56522                 text: cm.getColumnHeader(i),
56523                 checked: !cm.isHidden(i),
56524                 hideOnClick:false
56525             }));
56526         }
56527     },
56528
56529     handleHdCtx : function(g, index, e){
56530         e.stopEvent();
56531         var hd = this.getHeaderCell(index);
56532         this.hdCtxIndex = index;
56533         var ms = this.hmenu.items, cm = this.cm;
56534         ms.get("asc").setDisabled(!cm.isSortable(index));
56535         ms.get("desc").setDisabled(!cm.isSortable(index));
56536         if(this.grid.enableColLock !== false){
56537             ms.get("lock").setDisabled(cm.isLocked(index));
56538             ms.get("unlock").setDisabled(!cm.isLocked(index));
56539         }
56540         this.hmenu.show(hd, "tl-bl");
56541     },
56542
56543     handleHdOver : function(e){
56544         var hd = this.findHeaderCell(e.getTarget());
56545         if(hd && !this.headersDisabled){
56546             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
56547                this.fly(hd).addClass("x-grid-hd-over");
56548             }
56549         }
56550     },
56551
56552     handleHdOut : function(e){
56553         var hd = this.findHeaderCell(e.getTarget());
56554         if(hd){
56555             this.fly(hd).removeClass("x-grid-hd-over");
56556         }
56557     },
56558
56559     handleSplitDblClick : function(e, t){
56560         var i = this.getCellIndex(t);
56561         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
56562             this.autoSizeColumn(i, true);
56563             this.layout();
56564         }
56565     },
56566
56567     render : function(){
56568
56569         var cm = this.cm;
56570         var colCount = cm.getColumnCount();
56571
56572         if(this.grid.monitorWindowResize === true){
56573             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
56574         }
56575         var header = this.renderHeaders();
56576         var body = this.templates.body.apply({rows:""});
56577         var html = this.templates.master.apply({
56578             lockedBody: body,
56579             body: body,
56580             lockedHeader: header[0],
56581             header: header[1]
56582         });
56583
56584         //this.updateColumns();
56585
56586         this.grid.getGridEl().dom.innerHTML = html;
56587
56588         this.initElements();
56589         
56590         // a kludge to fix the random scolling effect in webkit
56591         this.el.on("scroll", function() {
56592             this.el.dom.scrollTop=0; // hopefully not recursive..
56593         },this);
56594
56595         this.scroller.on("scroll", this.handleScroll, this);
56596         this.lockedBody.on("mousewheel", this.handleWheel, this);
56597         this.mainBody.on("mousewheel", this.handleWheel, this);
56598
56599         this.mainHd.on("mouseover", this.handleHdOver, this);
56600         this.mainHd.on("mouseout", this.handleHdOut, this);
56601         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
56602                 {delegate: "."+this.splitClass});
56603
56604         this.lockedHd.on("mouseover", this.handleHdOver, this);
56605         this.lockedHd.on("mouseout", this.handleHdOut, this);
56606         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
56607                 {delegate: "."+this.splitClass});
56608
56609         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
56610             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56611         }
56612
56613         this.updateSplitters();
56614
56615         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
56616             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56617             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56618         }
56619
56620         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
56621             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
56622             this.hmenu.add(
56623                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
56624                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
56625             );
56626             if(this.grid.enableColLock !== false){
56627                 this.hmenu.add('-',
56628                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
56629                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
56630                 );
56631             }
56632             if (Roo.isTouch) {
56633                  this.hmenu.add('-',
56634                     {id:"wider", text: this.columnsWiderText},
56635                     {id:"narrow", text: this.columnsNarrowText }
56636                 );
56637                 
56638                  
56639             }
56640             
56641             if(this.grid.enableColumnHide !== false){
56642
56643                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
56644                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
56645                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
56646
56647                 this.hmenu.add('-',
56648                     {id:"columns", text: this.columnsText, menu: this.colMenu}
56649                 );
56650             }
56651             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
56652
56653             this.grid.on("headercontextmenu", this.handleHdCtx, this);
56654         }
56655
56656         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
56657             this.dd = new Roo.grid.GridDragZone(this.grid, {
56658                 ddGroup : this.grid.ddGroup || 'GridDD'
56659             });
56660             
56661         }
56662
56663         /*
56664         for(var i = 0; i < colCount; i++){
56665             if(cm.isHidden(i)){
56666                 this.hideColumn(i);
56667             }
56668             if(cm.config[i].align){
56669                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
56670                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
56671             }
56672         }*/
56673         
56674         this.updateHeaderSortState();
56675
56676         this.beforeInitialResize();
56677         this.layout(true);
56678
56679         // two part rendering gives faster view to the user
56680         this.renderPhase2.defer(1, this);
56681     },
56682
56683     renderPhase2 : function(){
56684         // render the rows now
56685         this.refresh();
56686         if(this.grid.autoSizeColumns){
56687             this.autoSizeColumns();
56688         }
56689     },
56690
56691     beforeInitialResize : function(){
56692
56693     },
56694
56695     onColumnSplitterMoved : function(i, w){
56696         this.userResized = true;
56697         var cm = this.grid.colModel;
56698         cm.setColumnWidth(i, w, true);
56699         var cid = cm.getColumnId(i);
56700         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56701         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56702         this.updateSplitters();
56703         this.layout();
56704         this.grid.fireEvent("columnresize", i, w);
56705     },
56706
56707     syncRowHeights : function(startIndex, endIndex){
56708         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
56709             startIndex = startIndex || 0;
56710             var mrows = this.getBodyTable().rows;
56711             var lrows = this.getLockedTable().rows;
56712             var len = mrows.length-1;
56713             endIndex = Math.min(endIndex || len, len);
56714             for(var i = startIndex; i <= endIndex; i++){
56715                 var m = mrows[i], l = lrows[i];
56716                 var h = Math.max(m.offsetHeight, l.offsetHeight);
56717                 m.style.height = l.style.height = h + "px";
56718             }
56719         }
56720     },
56721
56722     layout : function(initialRender, is2ndPass){
56723         var g = this.grid;
56724         var auto = g.autoHeight;
56725         var scrollOffset = 16;
56726         var c = g.getGridEl(), cm = this.cm,
56727                 expandCol = g.autoExpandColumn,
56728                 gv = this;
56729         //c.beginMeasure();
56730
56731         if(!c.dom.offsetWidth){ // display:none?
56732             if(initialRender){
56733                 this.lockedWrap.show();
56734                 this.mainWrap.show();
56735             }
56736             return;
56737         }
56738
56739         var hasLock = this.cm.isLocked(0);
56740
56741         var tbh = this.headerPanel.getHeight();
56742         var bbh = this.footerPanel.getHeight();
56743
56744         if(auto){
56745             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
56746             var newHeight = ch + c.getBorderWidth("tb");
56747             if(g.maxHeight){
56748                 newHeight = Math.min(g.maxHeight, newHeight);
56749             }
56750             c.setHeight(newHeight);
56751         }
56752
56753         if(g.autoWidth){
56754             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
56755         }
56756
56757         var s = this.scroller;
56758
56759         var csize = c.getSize(true);
56760
56761         this.el.setSize(csize.width, csize.height);
56762
56763         this.headerPanel.setWidth(csize.width);
56764         this.footerPanel.setWidth(csize.width);
56765
56766         var hdHeight = this.mainHd.getHeight();
56767         var vw = csize.width;
56768         var vh = csize.height - (tbh + bbh);
56769
56770         s.setSize(vw, vh);
56771
56772         var bt = this.getBodyTable();
56773         
56774         if(cm.getLockedCount() == cm.config.length){
56775             bt = this.getLockedTable();
56776         }
56777         
56778         var ltWidth = hasLock ?
56779                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
56780
56781         var scrollHeight = bt.offsetHeight;
56782         var scrollWidth = ltWidth + bt.offsetWidth;
56783         var vscroll = false, hscroll = false;
56784
56785         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
56786
56787         var lw = this.lockedWrap, mw = this.mainWrap;
56788         var lb = this.lockedBody, mb = this.mainBody;
56789
56790         setTimeout(function(){
56791             var t = s.dom.offsetTop;
56792             var w = s.dom.clientWidth,
56793                 h = s.dom.clientHeight;
56794
56795             lw.setTop(t);
56796             lw.setSize(ltWidth, h);
56797
56798             mw.setLeftTop(ltWidth, t);
56799             mw.setSize(w-ltWidth, h);
56800
56801             lb.setHeight(h-hdHeight);
56802             mb.setHeight(h-hdHeight);
56803
56804             if(is2ndPass !== true && !gv.userResized && expandCol){
56805                 // high speed resize without full column calculation
56806                 
56807                 var ci = cm.getIndexById(expandCol);
56808                 if (ci < 0) {
56809                     ci = cm.findColumnIndex(expandCol);
56810                 }
56811                 ci = Math.max(0, ci); // make sure it's got at least the first col.
56812                 var expandId = cm.getColumnId(ci);
56813                 var  tw = cm.getTotalWidth(false);
56814                 var currentWidth = cm.getColumnWidth(ci);
56815                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
56816                 if(currentWidth != cw){
56817                     cm.setColumnWidth(ci, cw, true);
56818                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56819                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56820                     gv.updateSplitters();
56821                     gv.layout(false, true);
56822                 }
56823             }
56824
56825             if(initialRender){
56826                 lw.show();
56827                 mw.show();
56828             }
56829             //c.endMeasure();
56830         }, 10);
56831     },
56832
56833     onWindowResize : function(){
56834         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
56835             return;
56836         }
56837         this.layout();
56838     },
56839
56840     appendFooter : function(parentEl){
56841         return null;
56842     },
56843
56844     sortAscText : "Sort Ascending",
56845     sortDescText : "Sort Descending",
56846     lockText : "Lock Column",
56847     unlockText : "Unlock Column",
56848     columnsText : "Columns",
56849  
56850     columnsWiderText : "Wider",
56851     columnsNarrowText : "Thinner"
56852 });
56853
56854
56855 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
56856     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
56857     this.proxy.el.addClass('x-grid3-col-dd');
56858 };
56859
56860 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
56861     handleMouseDown : function(e){
56862
56863     },
56864
56865     callHandleMouseDown : function(e){
56866         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
56867     }
56868 });
56869 /*
56870  * Based on:
56871  * Ext JS Library 1.1.1
56872  * Copyright(c) 2006-2007, Ext JS, LLC.
56873  *
56874  * Originally Released Under LGPL - original licence link has changed is not relivant.
56875  *
56876  * Fork - LGPL
56877  * <script type="text/javascript">
56878  */
56879  
56880 // private
56881 // This is a support class used internally by the Grid components
56882 Roo.grid.SplitDragZone = function(grid, hd, hd2){
56883     this.grid = grid;
56884     this.view = grid.getView();
56885     this.proxy = this.view.resizeProxy;
56886     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
56887         "gridSplitters" + this.grid.getGridEl().id, {
56888         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
56889     });
56890     this.setHandleElId(Roo.id(hd));
56891     this.setOuterHandleElId(Roo.id(hd2));
56892     this.scroll = false;
56893 };
56894 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
56895     fly: Roo.Element.fly,
56896
56897     b4StartDrag : function(x, y){
56898         this.view.headersDisabled = true;
56899         this.proxy.setHeight(this.view.mainWrap.getHeight());
56900         var w = this.cm.getColumnWidth(this.cellIndex);
56901         var minw = Math.max(w-this.grid.minColumnWidth, 0);
56902         this.resetConstraints();
56903         this.setXConstraint(minw, 1000);
56904         this.setYConstraint(0, 0);
56905         this.minX = x - minw;
56906         this.maxX = x + 1000;
56907         this.startPos = x;
56908         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
56909     },
56910
56911
56912     handleMouseDown : function(e){
56913         ev = Roo.EventObject.setEvent(e);
56914         var t = this.fly(ev.getTarget());
56915         if(t.hasClass("x-grid-split")){
56916             this.cellIndex = this.view.getCellIndex(t.dom);
56917             this.split = t.dom;
56918             this.cm = this.grid.colModel;
56919             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
56920                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
56921             }
56922         }
56923     },
56924
56925     endDrag : function(e){
56926         this.view.headersDisabled = false;
56927         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
56928         var diff = endX - this.startPos;
56929         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
56930     },
56931
56932     autoOffset : function(){
56933         this.setDelta(0,0);
56934     }
56935 });/*
56936  * Based on:
56937  * Ext JS Library 1.1.1
56938  * Copyright(c) 2006-2007, Ext JS, LLC.
56939  *
56940  * Originally Released Under LGPL - original licence link has changed is not relivant.
56941  *
56942  * Fork - LGPL
56943  * <script type="text/javascript">
56944  */
56945  
56946 // private
56947 // This is a support class used internally by the Grid components
56948 Roo.grid.GridDragZone = function(grid, config){
56949     this.view = grid.getView();
56950     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
56951     if(this.view.lockedBody){
56952         this.setHandleElId(Roo.id(this.view.mainBody.dom));
56953         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
56954     }
56955     this.scroll = false;
56956     this.grid = grid;
56957     this.ddel = document.createElement('div');
56958     this.ddel.className = 'x-grid-dd-wrap';
56959 };
56960
56961 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
56962     ddGroup : "GridDD",
56963
56964     getDragData : function(e){
56965         var t = Roo.lib.Event.getTarget(e);
56966         var rowIndex = this.view.findRowIndex(t);
56967         var sm = this.grid.selModel;
56968             
56969         //Roo.log(rowIndex);
56970         
56971         if (sm.getSelectedCell) {
56972             // cell selection..
56973             if (!sm.getSelectedCell()) {
56974                 return false;
56975             }
56976             if (rowIndex != sm.getSelectedCell()[0]) {
56977                 return false;
56978             }
56979         
56980         }
56981         
56982         if(rowIndex !== false){
56983             
56984             // if editorgrid.. 
56985             
56986             
56987             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
56988                
56989             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
56990               //  
56991             //}
56992             if (e.hasModifier()){
56993                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
56994             }
56995             
56996             Roo.log("getDragData");
56997             
56998             return {
56999                 grid: this.grid,
57000                 ddel: this.ddel,
57001                 rowIndex: rowIndex,
57002                 selections:sm.getSelections ? sm.getSelections() : (
57003                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
57004                 )
57005             };
57006         }
57007         return false;
57008     },
57009
57010     onInitDrag : function(e){
57011         var data = this.dragData;
57012         this.ddel.innerHTML = this.grid.getDragDropText();
57013         this.proxy.update(this.ddel);
57014         // fire start drag?
57015     },
57016
57017     afterRepair : function(){
57018         this.dragging = false;
57019     },
57020
57021     getRepairXY : function(e, data){
57022         return false;
57023     },
57024
57025     onEndDrag : function(data, e){
57026         // fire end drag?
57027     },
57028
57029     onValidDrop : function(dd, e, id){
57030         // fire drag drop?
57031         this.hideProxy();
57032     },
57033
57034     beforeInvalidDrop : function(e, id){
57035
57036     }
57037 });/*
57038  * Based on:
57039  * Ext JS Library 1.1.1
57040  * Copyright(c) 2006-2007, Ext JS, LLC.
57041  *
57042  * Originally Released Under LGPL - original licence link has changed is not relivant.
57043  *
57044  * Fork - LGPL
57045  * <script type="text/javascript">
57046  */
57047  
57048
57049 /**
57050  * @class Roo.grid.ColumnModel
57051  * @extends Roo.util.Observable
57052  * This is the default implementation of a ColumnModel used by the Grid. It defines
57053  * the columns in the grid.
57054  * <br>Usage:<br>
57055  <pre><code>
57056  var colModel = new Roo.grid.ColumnModel([
57057         {header: "Ticker", width: 60, sortable: true, locked: true},
57058         {header: "Company Name", width: 150, sortable: true},
57059         {header: "Market Cap.", width: 100, sortable: true},
57060         {header: "$ Sales", width: 100, sortable: true, renderer: money},
57061         {header: "Employees", width: 100, sortable: true, resizable: false}
57062  ]);
57063  </code></pre>
57064  * <p>
57065  
57066  * The config options listed for this class are options which may appear in each
57067  * individual column definition.
57068  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
57069  * @constructor
57070  * @param {Object} config An Array of column config objects. See this class's
57071  * config objects for details.
57072 */
57073 Roo.grid.ColumnModel = function(config){
57074         /**
57075      * The config passed into the constructor
57076      */
57077     this.config = config;
57078     this.lookup = {};
57079
57080     // if no id, create one
57081     // if the column does not have a dataIndex mapping,
57082     // map it to the order it is in the config
57083     for(var i = 0, len = config.length; i < len; i++){
57084         var c = config[i];
57085         if(typeof c.dataIndex == "undefined"){
57086             c.dataIndex = i;
57087         }
57088         if(typeof c.renderer == "string"){
57089             c.renderer = Roo.util.Format[c.renderer];
57090         }
57091         if(typeof c.id == "undefined"){
57092             c.id = Roo.id();
57093         }
57094         if(c.editor && c.editor.xtype){
57095             c.editor  = Roo.factory(c.editor, Roo.grid);
57096         }
57097         if(c.editor && c.editor.isFormField){
57098             c.editor = new Roo.grid.GridEditor(c.editor);
57099         }
57100         this.lookup[c.id] = c;
57101     }
57102
57103     /**
57104      * The width of columns which have no width specified (defaults to 100)
57105      * @type Number
57106      */
57107     this.defaultWidth = 100;
57108
57109     /**
57110      * Default sortable of columns which have no sortable specified (defaults to false)
57111      * @type Boolean
57112      */
57113     this.defaultSortable = false;
57114
57115     this.addEvents({
57116         /**
57117              * @event widthchange
57118              * Fires when the width of a column changes.
57119              * @param {ColumnModel} this
57120              * @param {Number} columnIndex The column index
57121              * @param {Number} newWidth The new width
57122              */
57123             "widthchange": true,
57124         /**
57125              * @event headerchange
57126              * Fires when the text of a header changes.
57127              * @param {ColumnModel} this
57128              * @param {Number} columnIndex The column index
57129              * @param {Number} newText The new header text
57130              */
57131             "headerchange": true,
57132         /**
57133              * @event hiddenchange
57134              * Fires when a column is hidden or "unhidden".
57135              * @param {ColumnModel} this
57136              * @param {Number} columnIndex The column index
57137              * @param {Boolean} hidden true if hidden, false otherwise
57138              */
57139             "hiddenchange": true,
57140             /**
57141          * @event columnmoved
57142          * Fires when a column is moved.
57143          * @param {ColumnModel} this
57144          * @param {Number} oldIndex
57145          * @param {Number} newIndex
57146          */
57147         "columnmoved" : true,
57148         /**
57149          * @event columlockchange
57150          * Fires when a column's locked state is changed
57151          * @param {ColumnModel} this
57152          * @param {Number} colIndex
57153          * @param {Boolean} locked true if locked
57154          */
57155         "columnlockchange" : true
57156     });
57157     Roo.grid.ColumnModel.superclass.constructor.call(this);
57158 };
57159 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
57160     /**
57161      * @cfg {String} header The header text to display in the Grid view.
57162      */
57163     /**
57164      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
57165      * {@link Roo.data.Record} definition from which to draw the column's value. If not
57166      * specified, the column's index is used as an index into the Record's data Array.
57167      */
57168     /**
57169      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
57170      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
57171      */
57172     /**
57173      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
57174      * Defaults to the value of the {@link #defaultSortable} property.
57175      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
57176      */
57177     /**
57178      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
57179      */
57180     /**
57181      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
57182      */
57183     /**
57184      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
57185      */
57186     /**
57187      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
57188      */
57189     /**
57190      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
57191      * given the cell's data value. See {@link #setRenderer}. If not specified, the
57192      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
57193      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
57194      */
57195        /**
57196      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
57197      */
57198     /**
57199      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
57200      */
57201     /**
57202      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
57203      */
57204     /**
57205      * @cfg {String} cursor (Optional)
57206      */
57207     /**
57208      * @cfg {String} tooltip (Optional)
57209      */
57210     /**
57211      * @cfg {Number} xs (Optional)
57212      */
57213     /**
57214      * @cfg {Number} sm (Optional)
57215      */
57216     /**
57217      * @cfg {Number} md (Optional)
57218      */
57219     /**
57220      * @cfg {Number} lg (Optional)
57221      */
57222     /**
57223      * Returns the id of the column at the specified index.
57224      * @param {Number} index The column index
57225      * @return {String} the id
57226      */
57227     getColumnId : function(index){
57228         return this.config[index].id;
57229     },
57230
57231     /**
57232      * Returns the column for a specified id.
57233      * @param {String} id The column id
57234      * @return {Object} the column
57235      */
57236     getColumnById : function(id){
57237         return this.lookup[id];
57238     },
57239
57240     
57241     /**
57242      * Returns the column for a specified dataIndex.
57243      * @param {String} dataIndex The column dataIndex
57244      * @return {Object|Boolean} the column or false if not found
57245      */
57246     getColumnByDataIndex: function(dataIndex){
57247         var index = this.findColumnIndex(dataIndex);
57248         return index > -1 ? this.config[index] : false;
57249     },
57250     
57251     /**
57252      * Returns the index for a specified column id.
57253      * @param {String} id The column id
57254      * @return {Number} the index, or -1 if not found
57255      */
57256     getIndexById : function(id){
57257         for(var i = 0, len = this.config.length; i < len; i++){
57258             if(this.config[i].id == id){
57259                 return i;
57260             }
57261         }
57262         return -1;
57263     },
57264     
57265     /**
57266      * Returns the index for a specified column dataIndex.
57267      * @param {String} dataIndex The column dataIndex
57268      * @return {Number} the index, or -1 if not found
57269      */
57270     
57271     findColumnIndex : function(dataIndex){
57272         for(var i = 0, len = this.config.length; i < len; i++){
57273             if(this.config[i].dataIndex == dataIndex){
57274                 return i;
57275             }
57276         }
57277         return -1;
57278     },
57279     
57280     
57281     moveColumn : function(oldIndex, newIndex){
57282         var c = this.config[oldIndex];
57283         this.config.splice(oldIndex, 1);
57284         this.config.splice(newIndex, 0, c);
57285         this.dataMap = null;
57286         this.fireEvent("columnmoved", this, oldIndex, newIndex);
57287     },
57288
57289     isLocked : function(colIndex){
57290         return this.config[colIndex].locked === true;
57291     },
57292
57293     setLocked : function(colIndex, value, suppressEvent){
57294         if(this.isLocked(colIndex) == value){
57295             return;
57296         }
57297         this.config[colIndex].locked = value;
57298         if(!suppressEvent){
57299             this.fireEvent("columnlockchange", this, colIndex, value);
57300         }
57301     },
57302
57303     getTotalLockedWidth : function(){
57304         var totalWidth = 0;
57305         for(var i = 0; i < this.config.length; i++){
57306             if(this.isLocked(i) && !this.isHidden(i)){
57307                 this.totalWidth += this.getColumnWidth(i);
57308             }
57309         }
57310         return totalWidth;
57311     },
57312
57313     getLockedCount : function(){
57314         for(var i = 0, len = this.config.length; i < len; i++){
57315             if(!this.isLocked(i)){
57316                 return i;
57317             }
57318         }
57319         
57320         return this.config.length;
57321     },
57322
57323     /**
57324      * Returns the number of columns.
57325      * @return {Number}
57326      */
57327     getColumnCount : function(visibleOnly){
57328         if(visibleOnly === true){
57329             var c = 0;
57330             for(var i = 0, len = this.config.length; i < len; i++){
57331                 if(!this.isHidden(i)){
57332                     c++;
57333                 }
57334             }
57335             return c;
57336         }
57337         return this.config.length;
57338     },
57339
57340     /**
57341      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
57342      * @param {Function} fn
57343      * @param {Object} scope (optional)
57344      * @return {Array} result
57345      */
57346     getColumnsBy : function(fn, scope){
57347         var r = [];
57348         for(var i = 0, len = this.config.length; i < len; i++){
57349             var c = this.config[i];
57350             if(fn.call(scope||this, c, i) === true){
57351                 r[r.length] = c;
57352             }
57353         }
57354         return r;
57355     },
57356
57357     /**
57358      * Returns true if the specified column is sortable.
57359      * @param {Number} col The column index
57360      * @return {Boolean}
57361      */
57362     isSortable : function(col){
57363         if(typeof this.config[col].sortable == "undefined"){
57364             return this.defaultSortable;
57365         }
57366         return this.config[col].sortable;
57367     },
57368
57369     /**
57370      * Returns the rendering (formatting) function defined for the column.
57371      * @param {Number} col The column index.
57372      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
57373      */
57374     getRenderer : function(col){
57375         if(!this.config[col].renderer){
57376             return Roo.grid.ColumnModel.defaultRenderer;
57377         }
57378         return this.config[col].renderer;
57379     },
57380
57381     /**
57382      * Sets the rendering (formatting) function for a column.
57383      * @param {Number} col The column index
57384      * @param {Function} fn The function to use to process the cell's raw data
57385      * to return HTML markup for the grid view. The render function is called with
57386      * the following parameters:<ul>
57387      * <li>Data value.</li>
57388      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
57389      * <li>css A CSS style string to apply to the table cell.</li>
57390      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
57391      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
57392      * <li>Row index</li>
57393      * <li>Column index</li>
57394      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
57395      */
57396     setRenderer : function(col, fn){
57397         this.config[col].renderer = fn;
57398     },
57399
57400     /**
57401      * Returns the width for the specified column.
57402      * @param {Number} col The column index
57403      * @return {Number}
57404      */
57405     getColumnWidth : function(col){
57406         return this.config[col].width * 1 || this.defaultWidth;
57407     },
57408
57409     /**
57410      * Sets the width for a column.
57411      * @param {Number} col The column index
57412      * @param {Number} width The new width
57413      */
57414     setColumnWidth : function(col, width, suppressEvent){
57415         this.config[col].width = width;
57416         this.totalWidth = null;
57417         if(!suppressEvent){
57418              this.fireEvent("widthchange", this, col, width);
57419         }
57420     },
57421
57422     /**
57423      * Returns the total width of all columns.
57424      * @param {Boolean} includeHidden True to include hidden column widths
57425      * @return {Number}
57426      */
57427     getTotalWidth : function(includeHidden){
57428         if(!this.totalWidth){
57429             this.totalWidth = 0;
57430             for(var i = 0, len = this.config.length; i < len; i++){
57431                 if(includeHidden || !this.isHidden(i)){
57432                     this.totalWidth += this.getColumnWidth(i);
57433                 }
57434             }
57435         }
57436         return this.totalWidth;
57437     },
57438
57439     /**
57440      * Returns the header for the specified column.
57441      * @param {Number} col The column index
57442      * @return {String}
57443      */
57444     getColumnHeader : function(col){
57445         return this.config[col].header;
57446     },
57447
57448     /**
57449      * Sets the header for a column.
57450      * @param {Number} col The column index
57451      * @param {String} header The new header
57452      */
57453     setColumnHeader : function(col, header){
57454         this.config[col].header = header;
57455         this.fireEvent("headerchange", this, col, header);
57456     },
57457
57458     /**
57459      * Returns the tooltip for the specified column.
57460      * @param {Number} col The column index
57461      * @return {String}
57462      */
57463     getColumnTooltip : function(col){
57464             return this.config[col].tooltip;
57465     },
57466     /**
57467      * Sets the tooltip for a column.
57468      * @param {Number} col The column index
57469      * @param {String} tooltip The new tooltip
57470      */
57471     setColumnTooltip : function(col, tooltip){
57472             this.config[col].tooltip = tooltip;
57473     },
57474
57475     /**
57476      * Returns the dataIndex for the specified column.
57477      * @param {Number} col The column index
57478      * @return {Number}
57479      */
57480     getDataIndex : function(col){
57481         return this.config[col].dataIndex;
57482     },
57483
57484     /**
57485      * Sets the dataIndex for a column.
57486      * @param {Number} col The column index
57487      * @param {Number} dataIndex The new dataIndex
57488      */
57489     setDataIndex : function(col, dataIndex){
57490         this.config[col].dataIndex = dataIndex;
57491     },
57492
57493     
57494     
57495     /**
57496      * Returns true if the cell is editable.
57497      * @param {Number} colIndex The column index
57498      * @param {Number} rowIndex The row index - this is nto actually used..?
57499      * @return {Boolean}
57500      */
57501     isCellEditable : function(colIndex, rowIndex){
57502         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
57503     },
57504
57505     /**
57506      * Returns the editor defined for the cell/column.
57507      * return false or null to disable editing.
57508      * @param {Number} colIndex The column index
57509      * @param {Number} rowIndex The row index
57510      * @return {Object}
57511      */
57512     getCellEditor : function(colIndex, rowIndex){
57513         return this.config[colIndex].editor;
57514     },
57515
57516     /**
57517      * Sets if a column is editable.
57518      * @param {Number} col The column index
57519      * @param {Boolean} editable True if the column is editable
57520      */
57521     setEditable : function(col, editable){
57522         this.config[col].editable = editable;
57523     },
57524
57525
57526     /**
57527      * Returns true if the column is hidden.
57528      * @param {Number} colIndex The column index
57529      * @return {Boolean}
57530      */
57531     isHidden : function(colIndex){
57532         return this.config[colIndex].hidden;
57533     },
57534
57535
57536     /**
57537      * Returns true if the column width cannot be changed
57538      */
57539     isFixed : function(colIndex){
57540         return this.config[colIndex].fixed;
57541     },
57542
57543     /**
57544      * Returns true if the column can be resized
57545      * @return {Boolean}
57546      */
57547     isResizable : function(colIndex){
57548         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
57549     },
57550     /**
57551      * Sets if a column is hidden.
57552      * @param {Number} colIndex The column index
57553      * @param {Boolean} hidden True if the column is hidden
57554      */
57555     setHidden : function(colIndex, hidden){
57556         this.config[colIndex].hidden = hidden;
57557         this.totalWidth = null;
57558         this.fireEvent("hiddenchange", this, colIndex, hidden);
57559     },
57560
57561     /**
57562      * Sets the editor for a column.
57563      * @param {Number} col The column index
57564      * @param {Object} editor The editor object
57565      */
57566     setEditor : function(col, editor){
57567         this.config[col].editor = editor;
57568     }
57569 });
57570
57571 Roo.grid.ColumnModel.defaultRenderer = function(value)
57572 {
57573     if(typeof value == "object") {
57574         return value;
57575     }
57576         if(typeof value == "string" && value.length < 1){
57577             return "&#160;";
57578         }
57579     
57580         return String.format("{0}", value);
57581 };
57582
57583 // Alias for backwards compatibility
57584 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
57585 /*
57586  * Based on:
57587  * Ext JS Library 1.1.1
57588  * Copyright(c) 2006-2007, Ext JS, LLC.
57589  *
57590  * Originally Released Under LGPL - original licence link has changed is not relivant.
57591  *
57592  * Fork - LGPL
57593  * <script type="text/javascript">
57594  */
57595
57596 /**
57597  * @class Roo.grid.AbstractSelectionModel
57598  * @extends Roo.util.Observable
57599  * Abstract base class for grid SelectionModels.  It provides the interface that should be
57600  * implemented by descendant classes.  This class should not be directly instantiated.
57601  * @constructor
57602  */
57603 Roo.grid.AbstractSelectionModel = function(){
57604     this.locked = false;
57605     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
57606 };
57607
57608 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
57609     /** @ignore Called by the grid automatically. Do not call directly. */
57610     init : function(grid){
57611         this.grid = grid;
57612         this.initEvents();
57613     },
57614
57615     /**
57616      * Locks the selections.
57617      */
57618     lock : function(){
57619         this.locked = true;
57620     },
57621
57622     /**
57623      * Unlocks the selections.
57624      */
57625     unlock : function(){
57626         this.locked = false;
57627     },
57628
57629     /**
57630      * Returns true if the selections are locked.
57631      * @return {Boolean}
57632      */
57633     isLocked : function(){
57634         return this.locked;
57635     }
57636 });/*
57637  * Based on:
57638  * Ext JS Library 1.1.1
57639  * Copyright(c) 2006-2007, Ext JS, LLC.
57640  *
57641  * Originally Released Under LGPL - original licence link has changed is not relivant.
57642  *
57643  * Fork - LGPL
57644  * <script type="text/javascript">
57645  */
57646 /**
57647  * @extends Roo.grid.AbstractSelectionModel
57648  * @class Roo.grid.RowSelectionModel
57649  * The default SelectionModel used by {@link Roo.grid.Grid}.
57650  * It supports multiple selections and keyboard selection/navigation. 
57651  * @constructor
57652  * @param {Object} config
57653  */
57654 Roo.grid.RowSelectionModel = function(config){
57655     Roo.apply(this, config);
57656     this.selections = new Roo.util.MixedCollection(false, function(o){
57657         return o.id;
57658     });
57659
57660     this.last = false;
57661     this.lastActive = false;
57662
57663     this.addEvents({
57664         /**
57665              * @event selectionchange
57666              * Fires when the selection changes
57667              * @param {SelectionModel} this
57668              */
57669             "selectionchange" : true,
57670         /**
57671              * @event afterselectionchange
57672              * Fires after the selection changes (eg. by key press or clicking)
57673              * @param {SelectionModel} this
57674              */
57675             "afterselectionchange" : true,
57676         /**
57677              * @event beforerowselect
57678              * Fires when a row is selected being selected, return false to cancel.
57679              * @param {SelectionModel} this
57680              * @param {Number} rowIndex The selected index
57681              * @param {Boolean} keepExisting False if other selections will be cleared
57682              */
57683             "beforerowselect" : true,
57684         /**
57685              * @event rowselect
57686              * Fires when a row is selected.
57687              * @param {SelectionModel} this
57688              * @param {Number} rowIndex The selected index
57689              * @param {Roo.data.Record} r The record
57690              */
57691             "rowselect" : true,
57692         /**
57693              * @event rowdeselect
57694              * Fires when a row is deselected.
57695              * @param {SelectionModel} this
57696              * @param {Number} rowIndex The selected index
57697              */
57698         "rowdeselect" : true
57699     });
57700     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
57701     this.locked = false;
57702 };
57703
57704 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
57705     /**
57706      * @cfg {Boolean} singleSelect
57707      * True to allow selection of only one row at a time (defaults to false)
57708      */
57709     singleSelect : false,
57710
57711     // private
57712     initEvents : function(){
57713
57714         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
57715             this.grid.on("mousedown", this.handleMouseDown, this);
57716         }else{ // allow click to work like normal
57717             this.grid.on("rowclick", this.handleDragableRowClick, this);
57718         }
57719
57720         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
57721             "up" : function(e){
57722                 if(!e.shiftKey){
57723                     this.selectPrevious(e.shiftKey);
57724                 }else if(this.last !== false && this.lastActive !== false){
57725                     var last = this.last;
57726                     this.selectRange(this.last,  this.lastActive-1);
57727                     this.grid.getView().focusRow(this.lastActive);
57728                     if(last !== false){
57729                         this.last = last;
57730                     }
57731                 }else{
57732                     this.selectFirstRow();
57733                 }
57734                 this.fireEvent("afterselectionchange", this);
57735             },
57736             "down" : function(e){
57737                 if(!e.shiftKey){
57738                     this.selectNext(e.shiftKey);
57739                 }else if(this.last !== false && this.lastActive !== false){
57740                     var last = this.last;
57741                     this.selectRange(this.last,  this.lastActive+1);
57742                     this.grid.getView().focusRow(this.lastActive);
57743                     if(last !== false){
57744                         this.last = last;
57745                     }
57746                 }else{
57747                     this.selectFirstRow();
57748                 }
57749                 this.fireEvent("afterselectionchange", this);
57750             },
57751             scope: this
57752         });
57753
57754         var view = this.grid.view;
57755         view.on("refresh", this.onRefresh, this);
57756         view.on("rowupdated", this.onRowUpdated, this);
57757         view.on("rowremoved", this.onRemove, this);
57758     },
57759
57760     // private
57761     onRefresh : function(){
57762         var ds = this.grid.dataSource, i, v = this.grid.view;
57763         var s = this.selections;
57764         s.each(function(r){
57765             if((i = ds.indexOfId(r.id)) != -1){
57766                 v.onRowSelect(i);
57767                 s.add(ds.getAt(i)); // updating the selection relate data
57768             }else{
57769                 s.remove(r);
57770             }
57771         });
57772     },
57773
57774     // private
57775     onRemove : function(v, index, r){
57776         this.selections.remove(r);
57777     },
57778
57779     // private
57780     onRowUpdated : function(v, index, r){
57781         if(this.isSelected(r)){
57782             v.onRowSelect(index);
57783         }
57784     },
57785
57786     /**
57787      * Select records.
57788      * @param {Array} records The records to select
57789      * @param {Boolean} keepExisting (optional) True to keep existing selections
57790      */
57791     selectRecords : function(records, keepExisting){
57792         if(!keepExisting){
57793             this.clearSelections();
57794         }
57795         var ds = this.grid.dataSource;
57796         for(var i = 0, len = records.length; i < len; i++){
57797             this.selectRow(ds.indexOf(records[i]), true);
57798         }
57799     },
57800
57801     /**
57802      * Gets the number of selected rows.
57803      * @return {Number}
57804      */
57805     getCount : function(){
57806         return this.selections.length;
57807     },
57808
57809     /**
57810      * Selects the first row in the grid.
57811      */
57812     selectFirstRow : function(){
57813         this.selectRow(0);
57814     },
57815
57816     /**
57817      * Select the last row.
57818      * @param {Boolean} keepExisting (optional) True to keep existing selections
57819      */
57820     selectLastRow : function(keepExisting){
57821         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
57822     },
57823
57824     /**
57825      * Selects the row immediately following the last selected row.
57826      * @param {Boolean} keepExisting (optional) True to keep existing selections
57827      */
57828     selectNext : function(keepExisting){
57829         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
57830             this.selectRow(this.last+1, keepExisting);
57831             this.grid.getView().focusRow(this.last);
57832         }
57833     },
57834
57835     /**
57836      * Selects the row that precedes the last selected row.
57837      * @param {Boolean} keepExisting (optional) True to keep existing selections
57838      */
57839     selectPrevious : function(keepExisting){
57840         if(this.last){
57841             this.selectRow(this.last-1, keepExisting);
57842             this.grid.getView().focusRow(this.last);
57843         }
57844     },
57845
57846     /**
57847      * Returns the selected records
57848      * @return {Array} Array of selected records
57849      */
57850     getSelections : function(){
57851         return [].concat(this.selections.items);
57852     },
57853
57854     /**
57855      * Returns the first selected record.
57856      * @return {Record}
57857      */
57858     getSelected : function(){
57859         return this.selections.itemAt(0);
57860     },
57861
57862
57863     /**
57864      * Clears all selections.
57865      */
57866     clearSelections : function(fast){
57867         if(this.locked) {
57868             return;
57869         }
57870         if(fast !== true){
57871             var ds = this.grid.dataSource;
57872             var s = this.selections;
57873             s.each(function(r){
57874                 this.deselectRow(ds.indexOfId(r.id));
57875             }, this);
57876             s.clear();
57877         }else{
57878             this.selections.clear();
57879         }
57880         this.last = false;
57881     },
57882
57883
57884     /**
57885      * Selects all rows.
57886      */
57887     selectAll : function(){
57888         if(this.locked) {
57889             return;
57890         }
57891         this.selections.clear();
57892         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
57893             this.selectRow(i, true);
57894         }
57895     },
57896
57897     /**
57898      * Returns True if there is a selection.
57899      * @return {Boolean}
57900      */
57901     hasSelection : function(){
57902         return this.selections.length > 0;
57903     },
57904
57905     /**
57906      * Returns True if the specified row is selected.
57907      * @param {Number/Record} record The record or index of the record to check
57908      * @return {Boolean}
57909      */
57910     isSelected : function(index){
57911         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
57912         return (r && this.selections.key(r.id) ? true : false);
57913     },
57914
57915     /**
57916      * Returns True if the specified record id is selected.
57917      * @param {String} id The id of record to check
57918      * @return {Boolean}
57919      */
57920     isIdSelected : function(id){
57921         return (this.selections.key(id) ? true : false);
57922     },
57923
57924     // private
57925     handleMouseDown : function(e, t){
57926         var view = this.grid.getView(), rowIndex;
57927         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
57928             return;
57929         };
57930         if(e.shiftKey && this.last !== false){
57931             var last = this.last;
57932             this.selectRange(last, rowIndex, e.ctrlKey);
57933             this.last = last; // reset the last
57934             view.focusRow(rowIndex);
57935         }else{
57936             var isSelected = this.isSelected(rowIndex);
57937             if(e.button !== 0 && isSelected){
57938                 view.focusRow(rowIndex);
57939             }else if(e.ctrlKey && isSelected){
57940                 this.deselectRow(rowIndex);
57941             }else if(!isSelected){
57942                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
57943                 view.focusRow(rowIndex);
57944             }
57945         }
57946         this.fireEvent("afterselectionchange", this);
57947     },
57948     // private
57949     handleDragableRowClick :  function(grid, rowIndex, e) 
57950     {
57951         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
57952             this.selectRow(rowIndex, false);
57953             grid.view.focusRow(rowIndex);
57954              this.fireEvent("afterselectionchange", this);
57955         }
57956     },
57957     
57958     /**
57959      * Selects multiple rows.
57960      * @param {Array} rows Array of the indexes of the row to select
57961      * @param {Boolean} keepExisting (optional) True to keep existing selections
57962      */
57963     selectRows : function(rows, keepExisting){
57964         if(!keepExisting){
57965             this.clearSelections();
57966         }
57967         for(var i = 0, len = rows.length; i < len; i++){
57968             this.selectRow(rows[i], true);
57969         }
57970     },
57971
57972     /**
57973      * Selects a range of rows. All rows in between startRow and endRow are also selected.
57974      * @param {Number} startRow The index of the first row in the range
57975      * @param {Number} endRow The index of the last row in the range
57976      * @param {Boolean} keepExisting (optional) True to retain existing selections
57977      */
57978     selectRange : function(startRow, endRow, keepExisting){
57979         if(this.locked) {
57980             return;
57981         }
57982         if(!keepExisting){
57983             this.clearSelections();
57984         }
57985         if(startRow <= endRow){
57986             for(var i = startRow; i <= endRow; i++){
57987                 this.selectRow(i, true);
57988             }
57989         }else{
57990             for(var i = startRow; i >= endRow; i--){
57991                 this.selectRow(i, true);
57992             }
57993         }
57994     },
57995
57996     /**
57997      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
57998      * @param {Number} startRow The index of the first row in the range
57999      * @param {Number} endRow The index of the last row in the range
58000      */
58001     deselectRange : function(startRow, endRow, preventViewNotify){
58002         if(this.locked) {
58003             return;
58004         }
58005         for(var i = startRow; i <= endRow; i++){
58006             this.deselectRow(i, preventViewNotify);
58007         }
58008     },
58009
58010     /**
58011      * Selects a row.
58012      * @param {Number} row The index of the row to select
58013      * @param {Boolean} keepExisting (optional) True to keep existing selections
58014      */
58015     selectRow : function(index, keepExisting, preventViewNotify){
58016         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
58017             return;
58018         }
58019         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
58020             if(!keepExisting || this.singleSelect){
58021                 this.clearSelections();
58022             }
58023             var r = this.grid.dataSource.getAt(index);
58024             this.selections.add(r);
58025             this.last = this.lastActive = index;
58026             if(!preventViewNotify){
58027                 this.grid.getView().onRowSelect(index);
58028             }
58029             this.fireEvent("rowselect", this, index, r);
58030             this.fireEvent("selectionchange", this);
58031         }
58032     },
58033
58034     /**
58035      * Deselects a row.
58036      * @param {Number} row The index of the row to deselect
58037      */
58038     deselectRow : function(index, preventViewNotify){
58039         if(this.locked) {
58040             return;
58041         }
58042         if(this.last == index){
58043             this.last = false;
58044         }
58045         if(this.lastActive == index){
58046             this.lastActive = false;
58047         }
58048         var r = this.grid.dataSource.getAt(index);
58049         this.selections.remove(r);
58050         if(!preventViewNotify){
58051             this.grid.getView().onRowDeselect(index);
58052         }
58053         this.fireEvent("rowdeselect", this, index);
58054         this.fireEvent("selectionchange", this);
58055     },
58056
58057     // private
58058     restoreLast : function(){
58059         if(this._last){
58060             this.last = this._last;
58061         }
58062     },
58063
58064     // private
58065     acceptsNav : function(row, col, cm){
58066         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58067     },
58068
58069     // private
58070     onEditorKey : function(field, e){
58071         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
58072         if(k == e.TAB){
58073             e.stopEvent();
58074             ed.completeEdit();
58075             if(e.shiftKey){
58076                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58077             }else{
58078                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58079             }
58080         }else if(k == e.ENTER && !e.ctrlKey){
58081             e.stopEvent();
58082             ed.completeEdit();
58083             if(e.shiftKey){
58084                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
58085             }else{
58086                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
58087             }
58088         }else if(k == e.ESC){
58089             ed.cancelEdit();
58090         }
58091         if(newCell){
58092             g.startEditing(newCell[0], newCell[1]);
58093         }
58094     }
58095 });/*
58096  * Based on:
58097  * Ext JS Library 1.1.1
58098  * Copyright(c) 2006-2007, Ext JS, LLC.
58099  *
58100  * Originally Released Under LGPL - original licence link has changed is not relivant.
58101  *
58102  * Fork - LGPL
58103  * <script type="text/javascript">
58104  */
58105 /**
58106  * @class Roo.grid.CellSelectionModel
58107  * @extends Roo.grid.AbstractSelectionModel
58108  * This class provides the basic implementation for cell selection in a grid.
58109  * @constructor
58110  * @param {Object} config The object containing the configuration of this model.
58111  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
58112  */
58113 Roo.grid.CellSelectionModel = function(config){
58114     Roo.apply(this, config);
58115
58116     this.selection = null;
58117
58118     this.addEvents({
58119         /**
58120              * @event beforerowselect
58121              * Fires before a cell is selected.
58122              * @param {SelectionModel} this
58123              * @param {Number} rowIndex The selected row index
58124              * @param {Number} colIndex The selected cell index
58125              */
58126             "beforecellselect" : true,
58127         /**
58128              * @event cellselect
58129              * Fires when a cell is selected.
58130              * @param {SelectionModel} this
58131              * @param {Number} rowIndex The selected row index
58132              * @param {Number} colIndex The selected cell index
58133              */
58134             "cellselect" : true,
58135         /**
58136              * @event selectionchange
58137              * Fires when the active selection changes.
58138              * @param {SelectionModel} this
58139              * @param {Object} selection null for no selection or an object (o) with two properties
58140                 <ul>
58141                 <li>o.record: the record object for the row the selection is in</li>
58142                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
58143                 </ul>
58144              */
58145             "selectionchange" : true,
58146         /**
58147              * @event tabend
58148              * Fires when the tab (or enter) was pressed on the last editable cell
58149              * You can use this to trigger add new row.
58150              * @param {SelectionModel} this
58151              */
58152             "tabend" : true,
58153          /**
58154              * @event beforeeditnext
58155              * Fires before the next editable sell is made active
58156              * You can use this to skip to another cell or fire the tabend
58157              *    if you set cell to false
58158              * @param {Object} eventdata object : { cell : [ row, col ] } 
58159              */
58160             "beforeeditnext" : true
58161     });
58162     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
58163 };
58164
58165 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
58166     
58167     enter_is_tab: false,
58168
58169     /** @ignore */
58170     initEvents : function(){
58171         this.grid.on("mousedown", this.handleMouseDown, this);
58172         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
58173         var view = this.grid.view;
58174         view.on("refresh", this.onViewChange, this);
58175         view.on("rowupdated", this.onRowUpdated, this);
58176         view.on("beforerowremoved", this.clearSelections, this);
58177         view.on("beforerowsinserted", this.clearSelections, this);
58178         if(this.grid.isEditor){
58179             this.grid.on("beforeedit", this.beforeEdit,  this);
58180         }
58181     },
58182
58183         //private
58184     beforeEdit : function(e){
58185         this.select(e.row, e.column, false, true, e.record);
58186     },
58187
58188         //private
58189     onRowUpdated : function(v, index, r){
58190         if(this.selection && this.selection.record == r){
58191             v.onCellSelect(index, this.selection.cell[1]);
58192         }
58193     },
58194
58195         //private
58196     onViewChange : function(){
58197         this.clearSelections(true);
58198     },
58199
58200         /**
58201          * Returns the currently selected cell,.
58202          * @return {Array} The selected cell (row, column) or null if none selected.
58203          */
58204     getSelectedCell : function(){
58205         return this.selection ? this.selection.cell : null;
58206     },
58207
58208     /**
58209      * Clears all selections.
58210      * @param {Boolean} true to prevent the gridview from being notified about the change.
58211      */
58212     clearSelections : function(preventNotify){
58213         var s = this.selection;
58214         if(s){
58215             if(preventNotify !== true){
58216                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
58217             }
58218             this.selection = null;
58219             this.fireEvent("selectionchange", this, null);
58220         }
58221     },
58222
58223     /**
58224      * Returns true if there is a selection.
58225      * @return {Boolean}
58226      */
58227     hasSelection : function(){
58228         return this.selection ? true : false;
58229     },
58230
58231     /** @ignore */
58232     handleMouseDown : function(e, t){
58233         var v = this.grid.getView();
58234         if(this.isLocked()){
58235             return;
58236         };
58237         var row = v.findRowIndex(t);
58238         var cell = v.findCellIndex(t);
58239         if(row !== false && cell !== false){
58240             this.select(row, cell);
58241         }
58242     },
58243
58244     /**
58245      * Selects a cell.
58246      * @param {Number} rowIndex
58247      * @param {Number} collIndex
58248      */
58249     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
58250         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
58251             this.clearSelections();
58252             r = r || this.grid.dataSource.getAt(rowIndex);
58253             this.selection = {
58254                 record : r,
58255                 cell : [rowIndex, colIndex]
58256             };
58257             if(!preventViewNotify){
58258                 var v = this.grid.getView();
58259                 v.onCellSelect(rowIndex, colIndex);
58260                 if(preventFocus !== true){
58261                     v.focusCell(rowIndex, colIndex);
58262                 }
58263             }
58264             this.fireEvent("cellselect", this, rowIndex, colIndex);
58265             this.fireEvent("selectionchange", this, this.selection);
58266         }
58267     },
58268
58269         //private
58270     isSelectable : function(rowIndex, colIndex, cm){
58271         return !cm.isHidden(colIndex);
58272     },
58273
58274     /** @ignore */
58275     handleKeyDown : function(e){
58276         //Roo.log('Cell Sel Model handleKeyDown');
58277         if(!e.isNavKeyPress()){
58278             return;
58279         }
58280         var g = this.grid, s = this.selection;
58281         if(!s){
58282             e.stopEvent();
58283             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
58284             if(cell){
58285                 this.select(cell[0], cell[1]);
58286             }
58287             return;
58288         }
58289         var sm = this;
58290         var walk = function(row, col, step){
58291             return g.walkCells(row, col, step, sm.isSelectable,  sm);
58292         };
58293         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
58294         var newCell;
58295
58296       
58297
58298         switch(k){
58299             case e.TAB:
58300                 // handled by onEditorKey
58301                 if (g.isEditor && g.editing) {
58302                     return;
58303                 }
58304                 if(e.shiftKey) {
58305                     newCell = walk(r, c-1, -1);
58306                 } else {
58307                     newCell = walk(r, c+1, 1);
58308                 }
58309                 break;
58310             
58311             case e.DOWN:
58312                newCell = walk(r+1, c, 1);
58313                 break;
58314             
58315             case e.UP:
58316                 newCell = walk(r-1, c, -1);
58317                 break;
58318             
58319             case e.RIGHT:
58320                 newCell = walk(r, c+1, 1);
58321                 break;
58322             
58323             case e.LEFT:
58324                 newCell = walk(r, c-1, -1);
58325                 break;
58326             
58327             case e.ENTER:
58328                 
58329                 if(g.isEditor && !g.editing){
58330                    g.startEditing(r, c);
58331                    e.stopEvent();
58332                    return;
58333                 }
58334                 
58335                 
58336              break;
58337         };
58338         if(newCell){
58339             this.select(newCell[0], newCell[1]);
58340             e.stopEvent();
58341             
58342         }
58343     },
58344
58345     acceptsNav : function(row, col, cm){
58346         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58347     },
58348     /**
58349      * Selects a cell.
58350      * @param {Number} field (not used) - as it's normally used as a listener
58351      * @param {Number} e - event - fake it by using
58352      *
58353      * var e = Roo.EventObjectImpl.prototype;
58354      * e.keyCode = e.TAB
58355      *
58356      * 
58357      */
58358     onEditorKey : function(field, e){
58359         
58360         var k = e.getKey(),
58361             newCell,
58362             g = this.grid,
58363             ed = g.activeEditor,
58364             forward = false;
58365         ///Roo.log('onEditorKey' + k);
58366         
58367         
58368         if (this.enter_is_tab && k == e.ENTER) {
58369             k = e.TAB;
58370         }
58371         
58372         if(k == e.TAB){
58373             if(e.shiftKey){
58374                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58375             }else{
58376                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58377                 forward = true;
58378             }
58379             
58380             e.stopEvent();
58381             
58382         } else if(k == e.ENTER &&  !e.ctrlKey){
58383             ed.completeEdit();
58384             e.stopEvent();
58385             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58386         
58387                 } else if(k == e.ESC){
58388             ed.cancelEdit();
58389         }
58390                 
58391         if (newCell) {
58392             var ecall = { cell : newCell, forward : forward };
58393             this.fireEvent('beforeeditnext', ecall );
58394             newCell = ecall.cell;
58395                         forward = ecall.forward;
58396         }
58397                 
58398         if(newCell){
58399             //Roo.log('next cell after edit');
58400             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
58401         } else if (forward) {
58402             // tabbed past last
58403             this.fireEvent.defer(100, this, ['tabend',this]);
58404         }
58405     }
58406 });/*
58407  * Based on:
58408  * Ext JS Library 1.1.1
58409  * Copyright(c) 2006-2007, Ext JS, LLC.
58410  *
58411  * Originally Released Under LGPL - original licence link has changed is not relivant.
58412  *
58413  * Fork - LGPL
58414  * <script type="text/javascript">
58415  */
58416  
58417 /**
58418  * @class Roo.grid.EditorGrid
58419  * @extends Roo.grid.Grid
58420  * Class for creating and editable grid.
58421  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
58422  * The container MUST have some type of size defined for the grid to fill. The container will be 
58423  * automatically set to position relative if it isn't already.
58424  * @param {Object} dataSource The data model to bind to
58425  * @param {Object} colModel The column model with info about this grid's columns
58426  */
58427 Roo.grid.EditorGrid = function(container, config){
58428     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
58429     this.getGridEl().addClass("xedit-grid");
58430
58431     if(!this.selModel){
58432         this.selModel = new Roo.grid.CellSelectionModel();
58433     }
58434
58435     this.activeEditor = null;
58436
58437         this.addEvents({
58438             /**
58439              * @event beforeedit
58440              * Fires before cell editing is triggered. The edit event object has the following properties <br />
58441              * <ul style="padding:5px;padding-left:16px;">
58442              * <li>grid - This grid</li>
58443              * <li>record - The record being edited</li>
58444              * <li>field - The field name being edited</li>
58445              * <li>value - The value for the field being edited.</li>
58446              * <li>row - The grid row index</li>
58447              * <li>column - The grid column index</li>
58448              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58449              * </ul>
58450              * @param {Object} e An edit event (see above for description)
58451              */
58452             "beforeedit" : true,
58453             /**
58454              * @event afteredit
58455              * Fires after a cell is edited. <br />
58456              * <ul style="padding:5px;padding-left:16px;">
58457              * <li>grid - This grid</li>
58458              * <li>record - The record being edited</li>
58459              * <li>field - The field name being edited</li>
58460              * <li>value - The value being set</li>
58461              * <li>originalValue - The original value for the field, before the edit.</li>
58462              * <li>row - The grid row index</li>
58463              * <li>column - The grid column index</li>
58464              * </ul>
58465              * @param {Object} e An edit event (see above for description)
58466              */
58467             "afteredit" : true,
58468             /**
58469              * @event validateedit
58470              * Fires after a cell is edited, but before the value is set in the record. 
58471          * You can use this to modify the value being set in the field, Return false
58472              * to cancel the change. The edit event object has the following properties <br />
58473              * <ul style="padding:5px;padding-left:16px;">
58474          * <li>editor - This editor</li>
58475              * <li>grid - This grid</li>
58476              * <li>record - The record being edited</li>
58477              * <li>field - The field name being edited</li>
58478              * <li>value - The value being set</li>
58479              * <li>originalValue - The original value for the field, before the edit.</li>
58480              * <li>row - The grid row index</li>
58481              * <li>column - The grid column index</li>
58482              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58483              * </ul>
58484              * @param {Object} e An edit event (see above for description)
58485              */
58486             "validateedit" : true
58487         });
58488     this.on("bodyscroll", this.stopEditing,  this);
58489     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
58490 };
58491
58492 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
58493     /**
58494      * @cfg {Number} clicksToEdit
58495      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
58496      */
58497     clicksToEdit: 2,
58498
58499     // private
58500     isEditor : true,
58501     // private
58502     trackMouseOver: false, // causes very odd FF errors
58503
58504     onCellDblClick : function(g, row, col){
58505         this.startEditing(row, col);
58506     },
58507
58508     onEditComplete : function(ed, value, startValue){
58509         this.editing = false;
58510         this.activeEditor = null;
58511         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
58512         var r = ed.record;
58513         var field = this.colModel.getDataIndex(ed.col);
58514         var e = {
58515             grid: this,
58516             record: r,
58517             field: field,
58518             originalValue: startValue,
58519             value: value,
58520             row: ed.row,
58521             column: ed.col,
58522             cancel:false,
58523             editor: ed
58524         };
58525         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
58526         cell.show();
58527           
58528         if(String(value) !== String(startValue)){
58529             
58530             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
58531                 r.set(field, e.value);
58532                 // if we are dealing with a combo box..
58533                 // then we also set the 'name' colum to be the displayField
58534                 if (ed.field.displayField && ed.field.name) {
58535                     r.set(ed.field.name, ed.field.el.dom.value);
58536                 }
58537                 
58538                 delete e.cancel; //?? why!!!
58539                 this.fireEvent("afteredit", e);
58540             }
58541         } else {
58542             this.fireEvent("afteredit", e); // always fire it!
58543         }
58544         this.view.focusCell(ed.row, ed.col);
58545     },
58546
58547     /**
58548      * Starts editing the specified for the specified row/column
58549      * @param {Number} rowIndex
58550      * @param {Number} colIndex
58551      */
58552     startEditing : function(row, col){
58553         this.stopEditing();
58554         if(this.colModel.isCellEditable(col, row)){
58555             this.view.ensureVisible(row, col, true);
58556           
58557             var r = this.dataSource.getAt(row);
58558             var field = this.colModel.getDataIndex(col);
58559             var cell = Roo.get(this.view.getCell(row,col));
58560             var e = {
58561                 grid: this,
58562                 record: r,
58563                 field: field,
58564                 value: r.data[field],
58565                 row: row,
58566                 column: col,
58567                 cancel:false 
58568             };
58569             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
58570                 this.editing = true;
58571                 var ed = this.colModel.getCellEditor(col, row);
58572                 
58573                 if (!ed) {
58574                     return;
58575                 }
58576                 if(!ed.rendered){
58577                     ed.render(ed.parentEl || document.body);
58578                 }
58579                 ed.field.reset();
58580                
58581                 cell.hide();
58582                 
58583                 (function(){ // complex but required for focus issues in safari, ie and opera
58584                     ed.row = row;
58585                     ed.col = col;
58586                     ed.record = r;
58587                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
58588                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
58589                     this.activeEditor = ed;
58590                     var v = r.data[field];
58591                     ed.startEdit(this.view.getCell(row, col), v);
58592                     // combo's with 'displayField and name set
58593                     if (ed.field.displayField && ed.field.name) {
58594                         ed.field.el.dom.value = r.data[ed.field.name];
58595                     }
58596                     
58597                     
58598                 }).defer(50, this);
58599             }
58600         }
58601     },
58602         
58603     /**
58604      * Stops any active editing
58605      */
58606     stopEditing : function(){
58607         if(this.activeEditor){
58608             this.activeEditor.completeEdit();
58609         }
58610         this.activeEditor = null;
58611     },
58612         
58613          /**
58614      * Called to get grid's drag proxy text, by default returns this.ddText.
58615      * @return {String}
58616      */
58617     getDragDropText : function(){
58618         var count = this.selModel.getSelectedCell() ? 1 : 0;
58619         return String.format(this.ddText, count, count == 1 ? '' : 's');
58620     }
58621         
58622 });/*
58623  * Based on:
58624  * Ext JS Library 1.1.1
58625  * Copyright(c) 2006-2007, Ext JS, LLC.
58626  *
58627  * Originally Released Under LGPL - original licence link has changed is not relivant.
58628  *
58629  * Fork - LGPL
58630  * <script type="text/javascript">
58631  */
58632
58633 // private - not really -- you end up using it !
58634 // This is a support class used internally by the Grid components
58635
58636 /**
58637  * @class Roo.grid.GridEditor
58638  * @extends Roo.Editor
58639  * Class for creating and editable grid elements.
58640  * @param {Object} config any settings (must include field)
58641  */
58642 Roo.grid.GridEditor = function(field, config){
58643     if (!config && field.field) {
58644         config = field;
58645         field = Roo.factory(config.field, Roo.form);
58646     }
58647     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
58648     field.monitorTab = false;
58649 };
58650
58651 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
58652     
58653     /**
58654      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
58655      */
58656     
58657     alignment: "tl-tl",
58658     autoSize: "width",
58659     hideEl : false,
58660     cls: "x-small-editor x-grid-editor",
58661     shim:false,
58662     shadow:"frame"
58663 });/*
58664  * Based on:
58665  * Ext JS Library 1.1.1
58666  * Copyright(c) 2006-2007, Ext JS, LLC.
58667  *
58668  * Originally Released Under LGPL - original licence link has changed is not relivant.
58669  *
58670  * Fork - LGPL
58671  * <script type="text/javascript">
58672  */
58673   
58674
58675   
58676 Roo.grid.PropertyRecord = Roo.data.Record.create([
58677     {name:'name',type:'string'},  'value'
58678 ]);
58679
58680
58681 Roo.grid.PropertyStore = function(grid, source){
58682     this.grid = grid;
58683     this.store = new Roo.data.Store({
58684         recordType : Roo.grid.PropertyRecord
58685     });
58686     this.store.on('update', this.onUpdate,  this);
58687     if(source){
58688         this.setSource(source);
58689     }
58690     Roo.grid.PropertyStore.superclass.constructor.call(this);
58691 };
58692
58693
58694
58695 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
58696     setSource : function(o){
58697         this.source = o;
58698         this.store.removeAll();
58699         var data = [];
58700         for(var k in o){
58701             if(this.isEditableValue(o[k])){
58702                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
58703             }
58704         }
58705         this.store.loadRecords({records: data}, {}, true);
58706     },
58707
58708     onUpdate : function(ds, record, type){
58709         if(type == Roo.data.Record.EDIT){
58710             var v = record.data['value'];
58711             var oldValue = record.modified['value'];
58712             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
58713                 this.source[record.id] = v;
58714                 record.commit();
58715                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
58716             }else{
58717                 record.reject();
58718             }
58719         }
58720     },
58721
58722     getProperty : function(row){
58723        return this.store.getAt(row);
58724     },
58725
58726     isEditableValue: function(val){
58727         if(val && val instanceof Date){
58728             return true;
58729         }else if(typeof val == 'object' || typeof val == 'function'){
58730             return false;
58731         }
58732         return true;
58733     },
58734
58735     setValue : function(prop, value){
58736         this.source[prop] = value;
58737         this.store.getById(prop).set('value', value);
58738     },
58739
58740     getSource : function(){
58741         return this.source;
58742     }
58743 });
58744
58745 Roo.grid.PropertyColumnModel = function(grid, store){
58746     this.grid = grid;
58747     var g = Roo.grid;
58748     g.PropertyColumnModel.superclass.constructor.call(this, [
58749         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
58750         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
58751     ]);
58752     this.store = store;
58753     this.bselect = Roo.DomHelper.append(document.body, {
58754         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
58755             {tag: 'option', value: 'true', html: 'true'},
58756             {tag: 'option', value: 'false', html: 'false'}
58757         ]
58758     });
58759     Roo.id(this.bselect);
58760     var f = Roo.form;
58761     this.editors = {
58762         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
58763         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
58764         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
58765         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
58766         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
58767     };
58768     this.renderCellDelegate = this.renderCell.createDelegate(this);
58769     this.renderPropDelegate = this.renderProp.createDelegate(this);
58770 };
58771
58772 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
58773     
58774     
58775     nameText : 'Name',
58776     valueText : 'Value',
58777     
58778     dateFormat : 'm/j/Y',
58779     
58780     
58781     renderDate : function(dateVal){
58782         return dateVal.dateFormat(this.dateFormat);
58783     },
58784
58785     renderBool : function(bVal){
58786         return bVal ? 'true' : 'false';
58787     },
58788
58789     isCellEditable : function(colIndex, rowIndex){
58790         return colIndex == 1;
58791     },
58792
58793     getRenderer : function(col){
58794         return col == 1 ?
58795             this.renderCellDelegate : this.renderPropDelegate;
58796     },
58797
58798     renderProp : function(v){
58799         return this.getPropertyName(v);
58800     },
58801
58802     renderCell : function(val){
58803         var rv = val;
58804         if(val instanceof Date){
58805             rv = this.renderDate(val);
58806         }else if(typeof val == 'boolean'){
58807             rv = this.renderBool(val);
58808         }
58809         return Roo.util.Format.htmlEncode(rv);
58810     },
58811
58812     getPropertyName : function(name){
58813         var pn = this.grid.propertyNames;
58814         return pn && pn[name] ? pn[name] : name;
58815     },
58816
58817     getCellEditor : function(colIndex, rowIndex){
58818         var p = this.store.getProperty(rowIndex);
58819         var n = p.data['name'], val = p.data['value'];
58820         
58821         if(typeof(this.grid.customEditors[n]) == 'string'){
58822             return this.editors[this.grid.customEditors[n]];
58823         }
58824         if(typeof(this.grid.customEditors[n]) != 'undefined'){
58825             return this.grid.customEditors[n];
58826         }
58827         if(val instanceof Date){
58828             return this.editors['date'];
58829         }else if(typeof val == 'number'){
58830             return this.editors['number'];
58831         }else if(typeof val == 'boolean'){
58832             return this.editors['boolean'];
58833         }else{
58834             return this.editors['string'];
58835         }
58836     }
58837 });
58838
58839 /**
58840  * @class Roo.grid.PropertyGrid
58841  * @extends Roo.grid.EditorGrid
58842  * This class represents the  interface of a component based property grid control.
58843  * <br><br>Usage:<pre><code>
58844  var grid = new Roo.grid.PropertyGrid("my-container-id", {
58845       
58846  });
58847  // set any options
58848  grid.render();
58849  * </code></pre>
58850   
58851  * @constructor
58852  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58853  * The container MUST have some type of size defined for the grid to fill. The container will be
58854  * automatically set to position relative if it isn't already.
58855  * @param {Object} config A config object that sets properties on this grid.
58856  */
58857 Roo.grid.PropertyGrid = function(container, config){
58858     config = config || {};
58859     var store = new Roo.grid.PropertyStore(this);
58860     this.store = store;
58861     var cm = new Roo.grid.PropertyColumnModel(this, store);
58862     store.store.sort('name', 'ASC');
58863     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
58864         ds: store.store,
58865         cm: cm,
58866         enableColLock:false,
58867         enableColumnMove:false,
58868         stripeRows:false,
58869         trackMouseOver: false,
58870         clicksToEdit:1
58871     }, config));
58872     this.getGridEl().addClass('x-props-grid');
58873     this.lastEditRow = null;
58874     this.on('columnresize', this.onColumnResize, this);
58875     this.addEvents({
58876          /**
58877              * @event beforepropertychange
58878              * Fires before a property changes (return false to stop?)
58879              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58880              * @param {String} id Record Id
58881              * @param {String} newval New Value
58882          * @param {String} oldval Old Value
58883              */
58884         "beforepropertychange": true,
58885         /**
58886              * @event propertychange
58887              * Fires after a property changes
58888              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58889              * @param {String} id Record Id
58890              * @param {String} newval New Value
58891          * @param {String} oldval Old Value
58892              */
58893         "propertychange": true
58894     });
58895     this.customEditors = this.customEditors || {};
58896 };
58897 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
58898     
58899      /**
58900      * @cfg {Object} customEditors map of colnames=> custom editors.
58901      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
58902      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
58903      * false disables editing of the field.
58904          */
58905     
58906       /**
58907      * @cfg {Object} propertyNames map of property Names to their displayed value
58908          */
58909     
58910     render : function(){
58911         Roo.grid.PropertyGrid.superclass.render.call(this);
58912         this.autoSize.defer(100, this);
58913     },
58914
58915     autoSize : function(){
58916         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
58917         if(this.view){
58918             this.view.fitColumns();
58919         }
58920     },
58921
58922     onColumnResize : function(){
58923         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
58924         this.autoSize();
58925     },
58926     /**
58927      * Sets the data for the Grid
58928      * accepts a Key => Value object of all the elements avaiable.
58929      * @param {Object} data  to appear in grid.
58930      */
58931     setSource : function(source){
58932         this.store.setSource(source);
58933         //this.autoSize();
58934     },
58935     /**
58936      * Gets all the data from the grid.
58937      * @return {Object} data  data stored in grid
58938      */
58939     getSource : function(){
58940         return this.store.getSource();
58941     }
58942 });/*
58943   
58944  * Licence LGPL
58945  
58946  */
58947  
58948 /**
58949  * @class Roo.grid.Calendar
58950  * @extends Roo.util.Grid
58951  * This class extends the Grid to provide a calendar widget
58952  * <br><br>Usage:<pre><code>
58953  var grid = new Roo.grid.Calendar("my-container-id", {
58954      ds: myDataStore,
58955      cm: myColModel,
58956      selModel: mySelectionModel,
58957      autoSizeColumns: true,
58958      monitorWindowResize: false,
58959      trackMouseOver: true
58960      eventstore : real data store..
58961  });
58962  // set any options
58963  grid.render();
58964   
58965   * @constructor
58966  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58967  * The container MUST have some type of size defined for the grid to fill. The container will be
58968  * automatically set to position relative if it isn't already.
58969  * @param {Object} config A config object that sets properties on this grid.
58970  */
58971 Roo.grid.Calendar = function(container, config){
58972         // initialize the container
58973         this.container = Roo.get(container);
58974         this.container.update("");
58975         this.container.setStyle("overflow", "hidden");
58976     this.container.addClass('x-grid-container');
58977
58978     this.id = this.container.id;
58979
58980     Roo.apply(this, config);
58981     // check and correct shorthanded configs
58982     
58983     var rows = [];
58984     var d =1;
58985     for (var r = 0;r < 6;r++) {
58986         
58987         rows[r]=[];
58988         for (var c =0;c < 7;c++) {
58989             rows[r][c]= '';
58990         }
58991     }
58992     if (this.eventStore) {
58993         this.eventStore= Roo.factory(this.eventStore, Roo.data);
58994         this.eventStore.on('load',this.onLoad, this);
58995         this.eventStore.on('beforeload',this.clearEvents, this);
58996          
58997     }
58998     
58999     this.dataSource = new Roo.data.Store({
59000             proxy: new Roo.data.MemoryProxy(rows),
59001             reader: new Roo.data.ArrayReader({}, [
59002                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
59003     });
59004
59005     this.dataSource.load();
59006     this.ds = this.dataSource;
59007     this.ds.xmodule = this.xmodule || false;
59008     
59009     
59010     var cellRender = function(v,x,r)
59011     {
59012         return String.format(
59013             '<div class="fc-day  fc-widget-content"><div>' +
59014                 '<div class="fc-event-container"></div>' +
59015                 '<div class="fc-day-number">{0}</div>'+
59016                 
59017                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
59018             '</div></div>', v);
59019     
59020     }
59021     
59022     
59023     this.colModel = new Roo.grid.ColumnModel( [
59024         {
59025             xtype: 'ColumnModel',
59026             xns: Roo.grid,
59027             dataIndex : 'weekday0',
59028             header : 'Sunday',
59029             renderer : cellRender
59030         },
59031         {
59032             xtype: 'ColumnModel',
59033             xns: Roo.grid,
59034             dataIndex : 'weekday1',
59035             header : 'Monday',
59036             renderer : cellRender
59037         },
59038         {
59039             xtype: 'ColumnModel',
59040             xns: Roo.grid,
59041             dataIndex : 'weekday2',
59042             header : 'Tuesday',
59043             renderer : cellRender
59044         },
59045         {
59046             xtype: 'ColumnModel',
59047             xns: Roo.grid,
59048             dataIndex : 'weekday3',
59049             header : 'Wednesday',
59050             renderer : cellRender
59051         },
59052         {
59053             xtype: 'ColumnModel',
59054             xns: Roo.grid,
59055             dataIndex : 'weekday4',
59056             header : 'Thursday',
59057             renderer : cellRender
59058         },
59059         {
59060             xtype: 'ColumnModel',
59061             xns: Roo.grid,
59062             dataIndex : 'weekday5',
59063             header : 'Friday',
59064             renderer : cellRender
59065         },
59066         {
59067             xtype: 'ColumnModel',
59068             xns: Roo.grid,
59069             dataIndex : 'weekday6',
59070             header : 'Saturday',
59071             renderer : cellRender
59072         }
59073     ]);
59074     this.cm = this.colModel;
59075     this.cm.xmodule = this.xmodule || false;
59076  
59077         
59078           
59079     //this.selModel = new Roo.grid.CellSelectionModel();
59080     //this.sm = this.selModel;
59081     //this.selModel.init(this);
59082     
59083     
59084     if(this.width){
59085         this.container.setWidth(this.width);
59086     }
59087
59088     if(this.height){
59089         this.container.setHeight(this.height);
59090     }
59091     /** @private */
59092         this.addEvents({
59093         // raw events
59094         /**
59095          * @event click
59096          * The raw click event for the entire grid.
59097          * @param {Roo.EventObject} e
59098          */
59099         "click" : true,
59100         /**
59101          * @event dblclick
59102          * The raw dblclick event for the entire grid.
59103          * @param {Roo.EventObject} e
59104          */
59105         "dblclick" : true,
59106         /**
59107          * @event contextmenu
59108          * The raw contextmenu event for the entire grid.
59109          * @param {Roo.EventObject} e
59110          */
59111         "contextmenu" : true,
59112         /**
59113          * @event mousedown
59114          * The raw mousedown event for the entire grid.
59115          * @param {Roo.EventObject} e
59116          */
59117         "mousedown" : true,
59118         /**
59119          * @event mouseup
59120          * The raw mouseup event for the entire grid.
59121          * @param {Roo.EventObject} e
59122          */
59123         "mouseup" : true,
59124         /**
59125          * @event mouseover
59126          * The raw mouseover event for the entire grid.
59127          * @param {Roo.EventObject} e
59128          */
59129         "mouseover" : true,
59130         /**
59131          * @event mouseout
59132          * The raw mouseout event for the entire grid.
59133          * @param {Roo.EventObject} e
59134          */
59135         "mouseout" : true,
59136         /**
59137          * @event keypress
59138          * The raw keypress event for the entire grid.
59139          * @param {Roo.EventObject} e
59140          */
59141         "keypress" : true,
59142         /**
59143          * @event keydown
59144          * The raw keydown event for the entire grid.
59145          * @param {Roo.EventObject} e
59146          */
59147         "keydown" : true,
59148
59149         // custom events
59150
59151         /**
59152          * @event cellclick
59153          * Fires when a cell is clicked
59154          * @param {Grid} this
59155          * @param {Number} rowIndex
59156          * @param {Number} columnIndex
59157          * @param {Roo.EventObject} e
59158          */
59159         "cellclick" : true,
59160         /**
59161          * @event celldblclick
59162          * Fires when a cell is double clicked
59163          * @param {Grid} this
59164          * @param {Number} rowIndex
59165          * @param {Number} columnIndex
59166          * @param {Roo.EventObject} e
59167          */
59168         "celldblclick" : true,
59169         /**
59170          * @event rowclick
59171          * Fires when a row is clicked
59172          * @param {Grid} this
59173          * @param {Number} rowIndex
59174          * @param {Roo.EventObject} e
59175          */
59176         "rowclick" : true,
59177         /**
59178          * @event rowdblclick
59179          * Fires when a row is double clicked
59180          * @param {Grid} this
59181          * @param {Number} rowIndex
59182          * @param {Roo.EventObject} e
59183          */
59184         "rowdblclick" : true,
59185         /**
59186          * @event headerclick
59187          * Fires when a header is clicked
59188          * @param {Grid} this
59189          * @param {Number} columnIndex
59190          * @param {Roo.EventObject} e
59191          */
59192         "headerclick" : true,
59193         /**
59194          * @event headerdblclick
59195          * Fires when a header cell is double clicked
59196          * @param {Grid} this
59197          * @param {Number} columnIndex
59198          * @param {Roo.EventObject} e
59199          */
59200         "headerdblclick" : true,
59201         /**
59202          * @event rowcontextmenu
59203          * Fires when a row is right clicked
59204          * @param {Grid} this
59205          * @param {Number} rowIndex
59206          * @param {Roo.EventObject} e
59207          */
59208         "rowcontextmenu" : true,
59209         /**
59210          * @event cellcontextmenu
59211          * Fires when a cell is right clicked
59212          * @param {Grid} this
59213          * @param {Number} rowIndex
59214          * @param {Number} cellIndex
59215          * @param {Roo.EventObject} e
59216          */
59217          "cellcontextmenu" : true,
59218         /**
59219          * @event headercontextmenu
59220          * Fires when a header is right clicked
59221          * @param {Grid} this
59222          * @param {Number} columnIndex
59223          * @param {Roo.EventObject} e
59224          */
59225         "headercontextmenu" : true,
59226         /**
59227          * @event bodyscroll
59228          * Fires when the body element is scrolled
59229          * @param {Number} scrollLeft
59230          * @param {Number} scrollTop
59231          */
59232         "bodyscroll" : true,
59233         /**
59234          * @event columnresize
59235          * Fires when the user resizes a column
59236          * @param {Number} columnIndex
59237          * @param {Number} newSize
59238          */
59239         "columnresize" : true,
59240         /**
59241          * @event columnmove
59242          * Fires when the user moves a column
59243          * @param {Number} oldIndex
59244          * @param {Number} newIndex
59245          */
59246         "columnmove" : true,
59247         /**
59248          * @event startdrag
59249          * Fires when row(s) start being dragged
59250          * @param {Grid} this
59251          * @param {Roo.GridDD} dd The drag drop object
59252          * @param {event} e The raw browser event
59253          */
59254         "startdrag" : true,
59255         /**
59256          * @event enddrag
59257          * Fires when a drag operation is complete
59258          * @param {Grid} this
59259          * @param {Roo.GridDD} dd The drag drop object
59260          * @param {event} e The raw browser event
59261          */
59262         "enddrag" : true,
59263         /**
59264          * @event dragdrop
59265          * Fires when dragged row(s) are dropped on a valid DD target
59266          * @param {Grid} this
59267          * @param {Roo.GridDD} dd The drag drop object
59268          * @param {String} targetId The target drag drop object
59269          * @param {event} e The raw browser event
59270          */
59271         "dragdrop" : true,
59272         /**
59273          * @event dragover
59274          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
59275          * @param {Grid} this
59276          * @param {Roo.GridDD} dd The drag drop object
59277          * @param {String} targetId The target drag drop object
59278          * @param {event} e The raw browser event
59279          */
59280         "dragover" : true,
59281         /**
59282          * @event dragenter
59283          *  Fires when the dragged row(s) first cross another DD target while being dragged
59284          * @param {Grid} this
59285          * @param {Roo.GridDD} dd The drag drop object
59286          * @param {String} targetId The target drag drop object
59287          * @param {event} e The raw browser event
59288          */
59289         "dragenter" : true,
59290         /**
59291          * @event dragout
59292          * Fires when the dragged row(s) leave another DD target while being dragged
59293          * @param {Grid} this
59294          * @param {Roo.GridDD} dd The drag drop object
59295          * @param {String} targetId The target drag drop object
59296          * @param {event} e The raw browser event
59297          */
59298         "dragout" : true,
59299         /**
59300          * @event rowclass
59301          * Fires when a row is rendered, so you can change add a style to it.
59302          * @param {GridView} gridview   The grid view
59303          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
59304          */
59305         'rowclass' : true,
59306
59307         /**
59308          * @event render
59309          * Fires when the grid is rendered
59310          * @param {Grid} grid
59311          */
59312         'render' : true,
59313             /**
59314              * @event select
59315              * Fires when a date is selected
59316              * @param {DatePicker} this
59317              * @param {Date} date The selected date
59318              */
59319         'select': true,
59320         /**
59321              * @event monthchange
59322              * Fires when the displayed month changes 
59323              * @param {DatePicker} this
59324              * @param {Date} date The selected month
59325              */
59326         'monthchange': true,
59327         /**
59328              * @event evententer
59329              * Fires when mouse over an event
59330              * @param {Calendar} this
59331              * @param {event} Event
59332              */
59333         'evententer': true,
59334         /**
59335              * @event eventleave
59336              * Fires when the mouse leaves an
59337              * @param {Calendar} this
59338              * @param {event}
59339              */
59340         'eventleave': true,
59341         /**
59342              * @event eventclick
59343              * Fires when the mouse click an
59344              * @param {Calendar} this
59345              * @param {event}
59346              */
59347         'eventclick': true,
59348         /**
59349              * @event eventrender
59350              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
59351              * @param {Calendar} this
59352              * @param {data} data to be modified
59353              */
59354         'eventrender': true
59355         
59356     });
59357
59358     Roo.grid.Grid.superclass.constructor.call(this);
59359     this.on('render', function() {
59360         this.view.el.addClass('x-grid-cal'); 
59361         
59362         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
59363
59364     },this);
59365     
59366     if (!Roo.grid.Calendar.style) {
59367         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
59368             
59369             
59370             '.x-grid-cal .x-grid-col' :  {
59371                 height: 'auto !important',
59372                 'vertical-align': 'top'
59373             },
59374             '.x-grid-cal  .fc-event-hori' : {
59375                 height: '14px'
59376             }
59377              
59378             
59379         }, Roo.id());
59380     }
59381
59382     
59383     
59384 };
59385 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
59386     /**
59387      * @cfg {Store} eventStore The store that loads events.
59388      */
59389     eventStore : 25,
59390
59391      
59392     activeDate : false,
59393     startDay : 0,
59394     autoWidth : true,
59395     monitorWindowResize : false,
59396
59397     
59398     resizeColumns : function() {
59399         var col = (this.view.el.getWidth() / 7) - 3;
59400         // loop through cols, and setWidth
59401         for(var i =0 ; i < 7 ; i++){
59402             this.cm.setColumnWidth(i, col);
59403         }
59404     },
59405      setDate :function(date) {
59406         
59407         Roo.log('setDate?');
59408         
59409         this.resizeColumns();
59410         var vd = this.activeDate;
59411         this.activeDate = date;
59412 //        if(vd && this.el){
59413 //            var t = date.getTime();
59414 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
59415 //                Roo.log('using add remove');
59416 //                
59417 //                this.fireEvent('monthchange', this, date);
59418 //                
59419 //                this.cells.removeClass("fc-state-highlight");
59420 //                this.cells.each(function(c){
59421 //                   if(c.dateValue == t){
59422 //                       c.addClass("fc-state-highlight");
59423 //                       setTimeout(function(){
59424 //                            try{c.dom.firstChild.focus();}catch(e){}
59425 //                       }, 50);
59426 //                       return false;
59427 //                   }
59428 //                   return true;
59429 //                });
59430 //                return;
59431 //            }
59432 //        }
59433         
59434         var days = date.getDaysInMonth();
59435         
59436         var firstOfMonth = date.getFirstDateOfMonth();
59437         var startingPos = firstOfMonth.getDay()-this.startDay;
59438         
59439         if(startingPos < this.startDay){
59440             startingPos += 7;
59441         }
59442         
59443         var pm = date.add(Date.MONTH, -1);
59444         var prevStart = pm.getDaysInMonth()-startingPos;
59445 //        
59446         
59447         
59448         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59449         
59450         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
59451         //this.cells.addClassOnOver('fc-state-hover');
59452         
59453         var cells = this.cells.elements;
59454         var textEls = this.textNodes;
59455         
59456         //Roo.each(cells, function(cell){
59457         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
59458         //});
59459         
59460         days += startingPos;
59461
59462         // convert everything to numbers so it's fast
59463         var day = 86400000;
59464         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
59465         //Roo.log(d);
59466         //Roo.log(pm);
59467         //Roo.log(prevStart);
59468         
59469         var today = new Date().clearTime().getTime();
59470         var sel = date.clearTime().getTime();
59471         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
59472         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
59473         var ddMatch = this.disabledDatesRE;
59474         var ddText = this.disabledDatesText;
59475         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
59476         var ddaysText = this.disabledDaysText;
59477         var format = this.format;
59478         
59479         var setCellClass = function(cal, cell){
59480             
59481             //Roo.log('set Cell Class');
59482             cell.title = "";
59483             var t = d.getTime();
59484             
59485             //Roo.log(d);
59486             
59487             
59488             cell.dateValue = t;
59489             if(t == today){
59490                 cell.className += " fc-today";
59491                 cell.className += " fc-state-highlight";
59492                 cell.title = cal.todayText;
59493             }
59494             if(t == sel){
59495                 // disable highlight in other month..
59496                 cell.className += " fc-state-highlight";
59497                 
59498             }
59499             // disabling
59500             if(t < min) {
59501                 //cell.className = " fc-state-disabled";
59502                 cell.title = cal.minText;
59503                 return;
59504             }
59505             if(t > max) {
59506                 //cell.className = " fc-state-disabled";
59507                 cell.title = cal.maxText;
59508                 return;
59509             }
59510             if(ddays){
59511                 if(ddays.indexOf(d.getDay()) != -1){
59512                     // cell.title = ddaysText;
59513                    // cell.className = " fc-state-disabled";
59514                 }
59515             }
59516             if(ddMatch && format){
59517                 var fvalue = d.dateFormat(format);
59518                 if(ddMatch.test(fvalue)){
59519                     cell.title = ddText.replace("%0", fvalue);
59520                    cell.className = " fc-state-disabled";
59521                 }
59522             }
59523             
59524             if (!cell.initialClassName) {
59525                 cell.initialClassName = cell.dom.className;
59526             }
59527             
59528             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
59529         };
59530
59531         var i = 0;
59532         
59533         for(; i < startingPos; i++) {
59534             cells[i].dayName =  (++prevStart);
59535             Roo.log(textEls[i]);
59536             d.setDate(d.getDate()+1);
59537             
59538             //cells[i].className = "fc-past fc-other-month";
59539             setCellClass(this, cells[i]);
59540         }
59541         
59542         var intDay = 0;
59543         
59544         for(; i < days; i++){
59545             intDay = i - startingPos + 1;
59546             cells[i].dayName =  (intDay);
59547             d.setDate(d.getDate()+1);
59548             
59549             cells[i].className = ''; // "x-date-active";
59550             setCellClass(this, cells[i]);
59551         }
59552         var extraDays = 0;
59553         
59554         for(; i < 42; i++) {
59555             //textEls[i].innerHTML = (++extraDays);
59556             
59557             d.setDate(d.getDate()+1);
59558             cells[i].dayName = (++extraDays);
59559             cells[i].className = "fc-future fc-other-month";
59560             setCellClass(this, cells[i]);
59561         }
59562         
59563         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
59564         
59565         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
59566         
59567         // this will cause all the cells to mis
59568         var rows= [];
59569         var i =0;
59570         for (var r = 0;r < 6;r++) {
59571             for (var c =0;c < 7;c++) {
59572                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
59573             }    
59574         }
59575         
59576         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59577         for(i=0;i<cells.length;i++) {
59578             
59579             this.cells.elements[i].dayName = cells[i].dayName ;
59580             this.cells.elements[i].className = cells[i].className;
59581             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
59582             this.cells.elements[i].title = cells[i].title ;
59583             this.cells.elements[i].dateValue = cells[i].dateValue ;
59584         }
59585         
59586         
59587         
59588         
59589         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
59590         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
59591         
59592         ////if(totalRows != 6){
59593             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
59594            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
59595        // }
59596         
59597         this.fireEvent('monthchange', this, date);
59598         
59599         
59600     },
59601  /**
59602      * Returns the grid's SelectionModel.
59603      * @return {SelectionModel}
59604      */
59605     getSelectionModel : function(){
59606         if(!this.selModel){
59607             this.selModel = new Roo.grid.CellSelectionModel();
59608         }
59609         return this.selModel;
59610     },
59611
59612     load: function() {
59613         this.eventStore.load()
59614         
59615         
59616         
59617     },
59618     
59619     findCell : function(dt) {
59620         dt = dt.clearTime().getTime();
59621         var ret = false;
59622         this.cells.each(function(c){
59623             //Roo.log("check " +c.dateValue + '?=' + dt);
59624             if(c.dateValue == dt){
59625                 ret = c;
59626                 return false;
59627             }
59628             return true;
59629         });
59630         
59631         return ret;
59632     },
59633     
59634     findCells : function(rec) {
59635         var s = rec.data.start_dt.clone().clearTime().getTime();
59636        // Roo.log(s);
59637         var e= rec.data.end_dt.clone().clearTime().getTime();
59638        // Roo.log(e);
59639         var ret = [];
59640         this.cells.each(function(c){
59641              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
59642             
59643             if(c.dateValue > e){
59644                 return ;
59645             }
59646             if(c.dateValue < s){
59647                 return ;
59648             }
59649             ret.push(c);
59650         });
59651         
59652         return ret;    
59653     },
59654     
59655     findBestRow: function(cells)
59656     {
59657         var ret = 0;
59658         
59659         for (var i =0 ; i < cells.length;i++) {
59660             ret  = Math.max(cells[i].rows || 0,ret);
59661         }
59662         return ret;
59663         
59664     },
59665     
59666     
59667     addItem : function(rec)
59668     {
59669         // look for vertical location slot in
59670         var cells = this.findCells(rec);
59671         
59672         rec.row = this.findBestRow(cells);
59673         
59674         // work out the location.
59675         
59676         var crow = false;
59677         var rows = [];
59678         for(var i =0; i < cells.length; i++) {
59679             if (!crow) {
59680                 crow = {
59681                     start : cells[i],
59682                     end :  cells[i]
59683                 };
59684                 continue;
59685             }
59686             if (crow.start.getY() == cells[i].getY()) {
59687                 // on same row.
59688                 crow.end = cells[i];
59689                 continue;
59690             }
59691             // different row.
59692             rows.push(crow);
59693             crow = {
59694                 start: cells[i],
59695                 end : cells[i]
59696             };
59697             
59698         }
59699         
59700         rows.push(crow);
59701         rec.els = [];
59702         rec.rows = rows;
59703         rec.cells = cells;
59704         for (var i = 0; i < cells.length;i++) {
59705             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
59706             
59707         }
59708         
59709         
59710     },
59711     
59712     clearEvents: function() {
59713         
59714         if (!this.eventStore.getCount()) {
59715             return;
59716         }
59717         // reset number of rows in cells.
59718         Roo.each(this.cells.elements, function(c){
59719             c.rows = 0;
59720         });
59721         
59722         this.eventStore.each(function(e) {
59723             this.clearEvent(e);
59724         },this);
59725         
59726     },
59727     
59728     clearEvent : function(ev)
59729     {
59730         if (ev.els) {
59731             Roo.each(ev.els, function(el) {
59732                 el.un('mouseenter' ,this.onEventEnter, this);
59733                 el.un('mouseleave' ,this.onEventLeave, this);
59734                 el.remove();
59735             },this);
59736             ev.els = [];
59737         }
59738     },
59739     
59740     
59741     renderEvent : function(ev,ctr) {
59742         if (!ctr) {
59743              ctr = this.view.el.select('.fc-event-container',true).first();
59744         }
59745         
59746          
59747         this.clearEvent(ev);
59748             //code
59749        
59750         
59751         
59752         ev.els = [];
59753         var cells = ev.cells;
59754         var rows = ev.rows;
59755         this.fireEvent('eventrender', this, ev);
59756         
59757         for(var i =0; i < rows.length; i++) {
59758             
59759             cls = '';
59760             if (i == 0) {
59761                 cls += ' fc-event-start';
59762             }
59763             if ((i+1) == rows.length) {
59764                 cls += ' fc-event-end';
59765             }
59766             
59767             //Roo.log(ev.data);
59768             // how many rows should it span..
59769             var cg = this.eventTmpl.append(ctr,Roo.apply({
59770                 fccls : cls
59771                 
59772             }, ev.data) , true);
59773             
59774             
59775             cg.on('mouseenter' ,this.onEventEnter, this, ev);
59776             cg.on('mouseleave' ,this.onEventLeave, this, ev);
59777             cg.on('click', this.onEventClick, this, ev);
59778             
59779             ev.els.push(cg);
59780             
59781             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
59782             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
59783             //Roo.log(cg);
59784              
59785             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
59786             cg.setWidth(ebox.right - sbox.x -2);
59787         }
59788     },
59789     
59790     renderEvents: function()
59791     {   
59792         // first make sure there is enough space..
59793         
59794         if (!this.eventTmpl) {
59795             this.eventTmpl = new Roo.Template(
59796                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
59797                     '<div class="fc-event-inner">' +
59798                         '<span class="fc-event-time">{time}</span>' +
59799                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
59800                     '</div>' +
59801                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
59802                 '</div>'
59803             );
59804                 
59805         }
59806                
59807         
59808         
59809         this.cells.each(function(c) {
59810             //Roo.log(c.select('.fc-day-content div',true).first());
59811             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
59812         });
59813         
59814         var ctr = this.view.el.select('.fc-event-container',true).first();
59815         
59816         var cls;
59817         this.eventStore.each(function(ev){
59818             
59819             this.renderEvent(ev);
59820              
59821              
59822         }, this);
59823         this.view.layout();
59824         
59825     },
59826     
59827     onEventEnter: function (e, el,event,d) {
59828         this.fireEvent('evententer', this, el, event);
59829     },
59830     
59831     onEventLeave: function (e, el,event,d) {
59832         this.fireEvent('eventleave', this, el, event);
59833     },
59834     
59835     onEventClick: function (e, el,event,d) {
59836         this.fireEvent('eventclick', this, el, event);
59837     },
59838     
59839     onMonthChange: function () {
59840         this.store.load();
59841     },
59842     
59843     onLoad: function () {
59844         
59845         //Roo.log('calendar onload');
59846 //         
59847         if(this.eventStore.getCount() > 0){
59848             
59849            
59850             
59851             this.eventStore.each(function(d){
59852                 
59853                 
59854                 // FIXME..
59855                 var add =   d.data;
59856                 if (typeof(add.end_dt) == 'undefined')  {
59857                     Roo.log("Missing End time in calendar data: ");
59858                     Roo.log(d);
59859                     return;
59860                 }
59861                 if (typeof(add.start_dt) == 'undefined')  {
59862                     Roo.log("Missing Start time in calendar data: ");
59863                     Roo.log(d);
59864                     return;
59865                 }
59866                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
59867                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
59868                 add.id = add.id || d.id;
59869                 add.title = add.title || '??';
59870                 
59871                 this.addItem(d);
59872                 
59873              
59874             },this);
59875         }
59876         
59877         this.renderEvents();
59878     }
59879     
59880
59881 });
59882 /*
59883  grid : {
59884                 xtype: 'Grid',
59885                 xns: Roo.grid,
59886                 listeners : {
59887                     render : function ()
59888                     {
59889                         _this.grid = this;
59890                         
59891                         if (!this.view.el.hasClass('course-timesheet')) {
59892                             this.view.el.addClass('course-timesheet');
59893                         }
59894                         if (this.tsStyle) {
59895                             this.ds.load({});
59896                             return; 
59897                         }
59898                         Roo.log('width');
59899                         Roo.log(_this.grid.view.el.getWidth());
59900                         
59901                         
59902                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
59903                             '.course-timesheet .x-grid-row' : {
59904                                 height: '80px'
59905                             },
59906                             '.x-grid-row td' : {
59907                                 'vertical-align' : 0
59908                             },
59909                             '.course-edit-link' : {
59910                                 'color' : 'blue',
59911                                 'text-overflow' : 'ellipsis',
59912                                 'overflow' : 'hidden',
59913                                 'white-space' : 'nowrap',
59914                                 'cursor' : 'pointer'
59915                             },
59916                             '.sub-link' : {
59917                                 'color' : 'green'
59918                             },
59919                             '.de-act-sup-link' : {
59920                                 'color' : 'purple',
59921                                 'text-decoration' : 'line-through'
59922                             },
59923                             '.de-act-link' : {
59924                                 'color' : 'red',
59925                                 'text-decoration' : 'line-through'
59926                             },
59927                             '.course-timesheet .course-highlight' : {
59928                                 'border-top-style': 'dashed !important',
59929                                 'border-bottom-bottom': 'dashed !important'
59930                             },
59931                             '.course-timesheet .course-item' : {
59932                                 'font-family'   : 'tahoma, arial, helvetica',
59933                                 'font-size'     : '11px',
59934                                 'overflow'      : 'hidden',
59935                                 'padding-left'  : '10px',
59936                                 'padding-right' : '10px',
59937                                 'padding-top' : '10px' 
59938                             }
59939                             
59940                         }, Roo.id());
59941                                 this.ds.load({});
59942                     }
59943                 },
59944                 autoWidth : true,
59945                 monitorWindowResize : false,
59946                 cellrenderer : function(v,x,r)
59947                 {
59948                     return v;
59949                 },
59950                 sm : {
59951                     xtype: 'CellSelectionModel',
59952                     xns: Roo.grid
59953                 },
59954                 dataSource : {
59955                     xtype: 'Store',
59956                     xns: Roo.data,
59957                     listeners : {
59958                         beforeload : function (_self, options)
59959                         {
59960                             options.params = options.params || {};
59961                             options.params._month = _this.monthField.getValue();
59962                             options.params.limit = 9999;
59963                             options.params['sort'] = 'when_dt';    
59964                             options.params['dir'] = 'ASC';    
59965                             this.proxy.loadResponse = this.loadResponse;
59966                             Roo.log("load?");
59967                             //this.addColumns();
59968                         },
59969                         load : function (_self, records, options)
59970                         {
59971                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
59972                                 // if you click on the translation.. you can edit it...
59973                                 var el = Roo.get(this);
59974                                 var id = el.dom.getAttribute('data-id');
59975                                 var d = el.dom.getAttribute('data-date');
59976                                 var t = el.dom.getAttribute('data-time');
59977                                 //var id = this.child('span').dom.textContent;
59978                                 
59979                                 //Roo.log(this);
59980                                 Pman.Dialog.CourseCalendar.show({
59981                                     id : id,
59982                                     when_d : d,
59983                                     when_t : t,
59984                                     productitem_active : id ? 1 : 0
59985                                 }, function() {
59986                                     _this.grid.ds.load({});
59987                                 });
59988                            
59989                            });
59990                            
59991                            _this.panel.fireEvent('resize', [ '', '' ]);
59992                         }
59993                     },
59994                     loadResponse : function(o, success, response){
59995                             // this is overridden on before load..
59996                             
59997                             Roo.log("our code?");       
59998                             //Roo.log(success);
59999                             //Roo.log(response)
60000                             delete this.activeRequest;
60001                             if(!success){
60002                                 this.fireEvent("loadexception", this, o, response);
60003                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
60004                                 return;
60005                             }
60006                             var result;
60007                             try {
60008                                 result = o.reader.read(response);
60009                             }catch(e){
60010                                 Roo.log("load exception?");
60011                                 this.fireEvent("loadexception", this, o, response, e);
60012                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
60013                                 return;
60014                             }
60015                             Roo.log("ready...");        
60016                             // loop through result.records;
60017                             // and set this.tdate[date] = [] << array of records..
60018                             _this.tdata  = {};
60019                             Roo.each(result.records, function(r){
60020                                 //Roo.log(r.data);
60021                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
60022                                     _this.tdata[r.data.when_dt.format('j')] = [];
60023                                 }
60024                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
60025                             });
60026                             
60027                             //Roo.log(_this.tdata);
60028                             
60029                             result.records = [];
60030                             result.totalRecords = 6;
60031                     
60032                             // let's generate some duumy records for the rows.
60033                             //var st = _this.dateField.getValue();
60034                             
60035                             // work out monday..
60036                             //st = st.add(Date.DAY, -1 * st.format('w'));
60037                             
60038                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60039                             
60040                             var firstOfMonth = date.getFirstDayOfMonth();
60041                             var days = date.getDaysInMonth();
60042                             var d = 1;
60043                             var firstAdded = false;
60044                             for (var i = 0; i < result.totalRecords ; i++) {
60045                                 //var d= st.add(Date.DAY, i);
60046                                 var row = {};
60047                                 var added = 0;
60048                                 for(var w = 0 ; w < 7 ; w++){
60049                                     if(!firstAdded && firstOfMonth != w){
60050                                         continue;
60051                                     }
60052                                     if(d > days){
60053                                         continue;
60054                                     }
60055                                     firstAdded = true;
60056                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
60057                                     row['weekday'+w] = String.format(
60058                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
60059                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
60060                                                     d,
60061                                                     date.format('Y-m-')+dd
60062                                                 );
60063                                     added++;
60064                                     if(typeof(_this.tdata[d]) != 'undefined'){
60065                                         Roo.each(_this.tdata[d], function(r){
60066                                             var is_sub = '';
60067                                             var deactive = '';
60068                                             var id = r.id;
60069                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
60070                                             if(r.parent_id*1>0){
60071                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
60072                                                 id = r.parent_id;
60073                                             }
60074                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
60075                                                 deactive = 'de-act-link';
60076                                             }
60077                                             
60078                                             row['weekday'+w] += String.format(
60079                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
60080                                                     id, //0
60081                                                     r.product_id_name, //1
60082                                                     r.when_dt.format('h:ia'), //2
60083                                                     is_sub, //3
60084                                                     deactive, //4
60085                                                     desc // 5
60086                                             );
60087                                         });
60088                                     }
60089                                     d++;
60090                                 }
60091                                 
60092                                 // only do this if something added..
60093                                 if(added > 0){ 
60094                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
60095                                 }
60096                                 
60097                                 
60098                                 // push it twice. (second one with an hour..
60099                                 
60100                             }
60101                             //Roo.log(result);
60102                             this.fireEvent("load", this, o, o.request.arg);
60103                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
60104                         },
60105                     sortInfo : {field: 'when_dt', direction : 'ASC' },
60106                     proxy : {
60107                         xtype: 'HttpProxy',
60108                         xns: Roo.data,
60109                         method : 'GET',
60110                         url : baseURL + '/Roo/Shop_course.php'
60111                     },
60112                     reader : {
60113                         xtype: 'JsonReader',
60114                         xns: Roo.data,
60115                         id : 'id',
60116                         fields : [
60117                             {
60118                                 'name': 'id',
60119                                 'type': 'int'
60120                             },
60121                             {
60122                                 'name': 'when_dt',
60123                                 'type': 'string'
60124                             },
60125                             {
60126                                 'name': 'end_dt',
60127                                 'type': 'string'
60128                             },
60129                             {
60130                                 'name': 'parent_id',
60131                                 'type': 'int'
60132                             },
60133                             {
60134                                 'name': 'product_id',
60135                                 'type': 'int'
60136                             },
60137                             {
60138                                 'name': 'productitem_id',
60139                                 'type': 'int'
60140                             },
60141                             {
60142                                 'name': 'guid',
60143                                 'type': 'int'
60144                             }
60145                         ]
60146                     }
60147                 },
60148                 toolbar : {
60149                     xtype: 'Toolbar',
60150                     xns: Roo,
60151                     items : [
60152                         {
60153                             xtype: 'Button',
60154                             xns: Roo.Toolbar,
60155                             listeners : {
60156                                 click : function (_self, e)
60157                                 {
60158                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60159                                     sd.setMonth(sd.getMonth()-1);
60160                                     _this.monthField.setValue(sd.format('Y-m-d'));
60161                                     _this.grid.ds.load({});
60162                                 }
60163                             },
60164                             text : "Back"
60165                         },
60166                         {
60167                             xtype: 'Separator',
60168                             xns: Roo.Toolbar
60169                         },
60170                         {
60171                             xtype: 'MonthField',
60172                             xns: Roo.form,
60173                             listeners : {
60174                                 render : function (_self)
60175                                 {
60176                                     _this.monthField = _self;
60177                                    // _this.monthField.set  today
60178                                 },
60179                                 select : function (combo, date)
60180                                 {
60181                                     _this.grid.ds.load({});
60182                                 }
60183                             },
60184                             value : (function() { return new Date(); })()
60185                         },
60186                         {
60187                             xtype: 'Separator',
60188                             xns: Roo.Toolbar
60189                         },
60190                         {
60191                             xtype: 'TextItem',
60192                             xns: Roo.Toolbar,
60193                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
60194                         },
60195                         {
60196                             xtype: 'Fill',
60197                             xns: Roo.Toolbar
60198                         },
60199                         {
60200                             xtype: 'Button',
60201                             xns: Roo.Toolbar,
60202                             listeners : {
60203                                 click : function (_self, e)
60204                                 {
60205                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60206                                     sd.setMonth(sd.getMonth()+1);
60207                                     _this.monthField.setValue(sd.format('Y-m-d'));
60208                                     _this.grid.ds.load({});
60209                                 }
60210                             },
60211                             text : "Next"
60212                         }
60213                     ]
60214                 },
60215                  
60216             }
60217         };
60218         
60219         *//*
60220  * Based on:
60221  * Ext JS Library 1.1.1
60222  * Copyright(c) 2006-2007, Ext JS, LLC.
60223  *
60224  * Originally Released Under LGPL - original licence link has changed is not relivant.
60225  *
60226  * Fork - LGPL
60227  * <script type="text/javascript">
60228  */
60229  
60230 /**
60231  * @class Roo.LoadMask
60232  * A simple utility class for generically masking elements while loading data.  If the element being masked has
60233  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
60234  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
60235  * element's UpdateManager load indicator and will be destroyed after the initial load.
60236  * @constructor
60237  * Create a new LoadMask
60238  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
60239  * @param {Object} config The config object
60240  */
60241 Roo.LoadMask = function(el, config){
60242     this.el = Roo.get(el);
60243     Roo.apply(this, config);
60244     if(this.store){
60245         this.store.on('beforeload', this.onBeforeLoad, this);
60246         this.store.on('load', this.onLoad, this);
60247         this.store.on('loadexception', this.onLoadException, this);
60248         this.removeMask = false;
60249     }else{
60250         var um = this.el.getUpdateManager();
60251         um.showLoadIndicator = false; // disable the default indicator
60252         um.on('beforeupdate', this.onBeforeLoad, this);
60253         um.on('update', this.onLoad, this);
60254         um.on('failure', this.onLoad, this);
60255         this.removeMask = true;
60256     }
60257 };
60258
60259 Roo.LoadMask.prototype = {
60260     /**
60261      * @cfg {Boolean} removeMask
60262      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
60263      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
60264      */
60265     /**
60266      * @cfg {String} msg
60267      * The text to display in a centered loading message box (defaults to 'Loading...')
60268      */
60269     msg : 'Loading...',
60270     /**
60271      * @cfg {String} msgCls
60272      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
60273      */
60274     msgCls : 'x-mask-loading',
60275
60276     /**
60277      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
60278      * @type Boolean
60279      */
60280     disabled: false,
60281
60282     /**
60283      * Disables the mask to prevent it from being displayed
60284      */
60285     disable : function(){
60286        this.disabled = true;
60287     },
60288
60289     /**
60290      * Enables the mask so that it can be displayed
60291      */
60292     enable : function(){
60293         this.disabled = false;
60294     },
60295     
60296     onLoadException : function()
60297     {
60298         Roo.log(arguments);
60299         
60300         if (typeof(arguments[3]) != 'undefined') {
60301             Roo.MessageBox.alert("Error loading",arguments[3]);
60302         } 
60303         /*
60304         try {
60305             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
60306                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
60307             }   
60308         } catch(e) {
60309             
60310         }
60311         */
60312     
60313         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60314     },
60315     // private
60316     onLoad : function()
60317     {
60318         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60319     },
60320
60321     // private
60322     onBeforeLoad : function(){
60323         if(!this.disabled){
60324             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
60325         }
60326     },
60327
60328     // private
60329     destroy : function(){
60330         if(this.store){
60331             this.store.un('beforeload', this.onBeforeLoad, this);
60332             this.store.un('load', this.onLoad, this);
60333             this.store.un('loadexception', this.onLoadException, this);
60334         }else{
60335             var um = this.el.getUpdateManager();
60336             um.un('beforeupdate', this.onBeforeLoad, this);
60337             um.un('update', this.onLoad, this);
60338             um.un('failure', this.onLoad, this);
60339         }
60340     }
60341 };/*
60342  * Based on:
60343  * Ext JS Library 1.1.1
60344  * Copyright(c) 2006-2007, Ext JS, LLC.
60345  *
60346  * Originally Released Under LGPL - original licence link has changed is not relivant.
60347  *
60348  * Fork - LGPL
60349  * <script type="text/javascript">
60350  */
60351
60352
60353 /**
60354  * @class Roo.XTemplate
60355  * @extends Roo.Template
60356  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
60357 <pre><code>
60358 var t = new Roo.XTemplate(
60359         '&lt;select name="{name}"&gt;',
60360                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
60361         '&lt;/select&gt;'
60362 );
60363  
60364 // then append, applying the master template values
60365  </code></pre>
60366  *
60367  * Supported features:
60368  *
60369  *  Tags:
60370
60371 <pre><code>
60372       {a_variable} - output encoded.
60373       {a_variable.format:("Y-m-d")} - call a method on the variable
60374       {a_variable:raw} - unencoded output
60375       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
60376       {a_variable:this.method_on_template(...)} - call a method on the template object.
60377  
60378 </code></pre>
60379  *  The tpl tag:
60380 <pre><code>
60381         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
60382         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
60383         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
60384         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
60385   
60386         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
60387         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
60388 </code></pre>
60389  *      
60390  */
60391 Roo.XTemplate = function()
60392 {
60393     Roo.XTemplate.superclass.constructor.apply(this, arguments);
60394     if (this.html) {
60395         this.compile();
60396     }
60397 };
60398
60399
60400 Roo.extend(Roo.XTemplate, Roo.Template, {
60401
60402     /**
60403      * The various sub templates
60404      */
60405     tpls : false,
60406     /**
60407      *
60408      * basic tag replacing syntax
60409      * WORD:WORD()
60410      *
60411      * // you can fake an object call by doing this
60412      *  x.t:(test,tesT) 
60413      * 
60414      */
60415     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
60416
60417     /**
60418      * compile the template
60419      *
60420      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
60421      *
60422      */
60423     compile: function()
60424     {
60425         var s = this.html;
60426      
60427         s = ['<tpl>', s, '</tpl>'].join('');
60428     
60429         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
60430             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
60431             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
60432             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
60433             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
60434             m,
60435             id     = 0,
60436             tpls   = [];
60437     
60438         while(true == !!(m = s.match(re))){
60439             var forMatch   = m[0].match(nameRe),
60440                 ifMatch   = m[0].match(ifRe),
60441                 execMatch   = m[0].match(execRe),
60442                 namedMatch   = m[0].match(namedRe),
60443                 
60444                 exp  = null, 
60445                 fn   = null,
60446                 exec = null,
60447                 name = forMatch && forMatch[1] ? forMatch[1] : '';
60448                 
60449             if (ifMatch) {
60450                 // if - puts fn into test..
60451                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
60452                 if(exp){
60453                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
60454                 }
60455             }
60456             
60457             if (execMatch) {
60458                 // exec - calls a function... returns empty if true is  returned.
60459                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
60460                 if(exp){
60461                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
60462                 }
60463             }
60464             
60465             
60466             if (name) {
60467                 // for = 
60468                 switch(name){
60469                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
60470                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
60471                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
60472                 }
60473             }
60474             var uid = namedMatch ? namedMatch[1] : id;
60475             
60476             
60477             tpls.push({
60478                 id:     namedMatch ? namedMatch[1] : id,
60479                 target: name,
60480                 exec:   exec,
60481                 test:   fn,
60482                 body:   m[1] || ''
60483             });
60484             if (namedMatch) {
60485                 s = s.replace(m[0], '');
60486             } else { 
60487                 s = s.replace(m[0], '{xtpl'+ id + '}');
60488             }
60489             ++id;
60490         }
60491         this.tpls = [];
60492         for(var i = tpls.length-1; i >= 0; --i){
60493             this.compileTpl(tpls[i]);
60494             this.tpls[tpls[i].id] = tpls[i];
60495         }
60496         this.master = tpls[tpls.length-1];
60497         return this;
60498     },
60499     /**
60500      * same as applyTemplate, except it's done to one of the subTemplates
60501      * when using named templates, you can do:
60502      *
60503      * var str = pl.applySubTemplate('your-name', values);
60504      *
60505      * 
60506      * @param {Number} id of the template
60507      * @param {Object} values to apply to template
60508      * @param {Object} parent (normaly the instance of this object)
60509      */
60510     applySubTemplate : function(id, values, parent)
60511     {
60512         
60513         
60514         var t = this.tpls[id];
60515         
60516         
60517         try { 
60518             if(t.test && !t.test.call(this, values, parent)){
60519                 return '';
60520             }
60521         } catch(e) {
60522             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
60523             Roo.log(e.toString());
60524             Roo.log(t.test);
60525             return ''
60526         }
60527         try { 
60528             
60529             if(t.exec && t.exec.call(this, values, parent)){
60530                 return '';
60531             }
60532         } catch(e) {
60533             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
60534             Roo.log(e.toString());
60535             Roo.log(t.exec);
60536             return ''
60537         }
60538         try {
60539             var vs = t.target ? t.target.call(this, values, parent) : values;
60540             parent = t.target ? values : parent;
60541             if(t.target && vs instanceof Array){
60542                 var buf = [];
60543                 for(var i = 0, len = vs.length; i < len; i++){
60544                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
60545                 }
60546                 return buf.join('');
60547             }
60548             return t.compiled.call(this, vs, parent);
60549         } catch (e) {
60550             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
60551             Roo.log(e.toString());
60552             Roo.log(t.compiled);
60553             return '';
60554         }
60555     },
60556
60557     compileTpl : function(tpl)
60558     {
60559         var fm = Roo.util.Format;
60560         var useF = this.disableFormats !== true;
60561         var sep = Roo.isGecko ? "+" : ",";
60562         var undef = function(str) {
60563             Roo.log("Property not found :"  + str);
60564             return '';
60565         };
60566         
60567         var fn = function(m, name, format, args)
60568         {
60569             //Roo.log(arguments);
60570             args = args ? args.replace(/\\'/g,"'") : args;
60571             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
60572             if (typeof(format) == 'undefined') {
60573                 format= 'htmlEncode';
60574             }
60575             if (format == 'raw' ) {
60576                 format = false;
60577             }
60578             
60579             if(name.substr(0, 4) == 'xtpl'){
60580                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
60581             }
60582             
60583             // build an array of options to determine if value is undefined..
60584             
60585             // basically get 'xxxx.yyyy' then do
60586             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
60587             //    (function () { Roo.log("Property not found"); return ''; })() :
60588             //    ......
60589             
60590             var udef_ar = [];
60591             var lookfor = '';
60592             Roo.each(name.split('.'), function(st) {
60593                 lookfor += (lookfor.length ? '.': '') + st;
60594                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
60595             });
60596             
60597             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
60598             
60599             
60600             if(format && useF){
60601                 
60602                 args = args ? ',' + args : "";
60603                  
60604                 if(format.substr(0, 5) != "this."){
60605                     format = "fm." + format + '(';
60606                 }else{
60607                     format = 'this.call("'+ format.substr(5) + '", ';
60608                     args = ", values";
60609                 }
60610                 
60611                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
60612             }
60613              
60614             if (args.length) {
60615                 // called with xxyx.yuu:(test,test)
60616                 // change to ()
60617                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
60618             }
60619             // raw.. - :raw modifier..
60620             return "'"+ sep + udef_st  + name + ")"+sep+"'";
60621             
60622         };
60623         var body;
60624         // branched to use + in gecko and [].join() in others
60625         if(Roo.isGecko){
60626             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
60627                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
60628                     "';};};";
60629         }else{
60630             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
60631             body.push(tpl.body.replace(/(\r\n|\n)/g,
60632                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
60633             body.push("'].join('');};};");
60634             body = body.join('');
60635         }
60636         
60637         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
60638        
60639         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
60640         eval(body);
60641         
60642         return this;
60643     },
60644
60645     applyTemplate : function(values){
60646         return this.master.compiled.call(this, values, {});
60647         //var s = this.subs;
60648     },
60649
60650     apply : function(){
60651         return this.applyTemplate.apply(this, arguments);
60652     }
60653
60654  });
60655
60656 Roo.XTemplate.from = function(el){
60657     el = Roo.getDom(el);
60658     return new Roo.XTemplate(el.value || el.innerHTML);
60659 };