roojs-core.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         isGecko = !isSafari && ua.indexOf("gecko") > -1,
61         isBorderBox = isIE && !isStrict,
62         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
63         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
64         isLinux = (ua.indexOf("linux") != -1),
65         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
66         isIOS = /iphone|ipad/.test(ua),
67         isAndroid = /android/.test(ua),
68         isTouch =  (function() {
69             try {
70                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
71                     window.addEventListener('touchstart', function __set_has_touch__ () {
72                         Roo.isTouch = true;
73                         window.removeEventListener('touchstart', __set_has_touch__);
74                     });
75                     return false; // no touch on chrome!?
76                 }
77                 document.createEvent("TouchEvent");  
78                 return true;  
79             } catch (e) {  
80                 return false;  
81             } 
82             
83         })();
84     // remove css image flicker
85         if(isIE && !isIE7){
86         try{
87             document.execCommand("BackgroundImageCache", false, true);
88         }catch(e){}
89     }
90     
91     Roo.apply(Roo, {
92         /**
93          * True if the browser is in strict mode
94          * @type Boolean
95          */
96         isStrict : isStrict,
97         /**
98          * True if the page is running over SSL
99          * @type Boolean
100          */
101         isSecure : isSecure,
102         /**
103          * True when the document is fully initialized and ready for action
104          * @type Boolean
105          */
106         isReady : false,
107         /**
108          * Turn on debugging output (currently only the factory uses this)
109          * @type Boolean
110          */
111         
112         debug: false,
113
114         /**
115          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
116          * @type Boolean
117          */
118         enableGarbageCollector : true,
119
120         /**
121          * True to automatically purge event listeners after uncaching an element (defaults to false).
122          * Note: this only happens if enableGarbageCollector is true.
123          * @type Boolean
124          */
125         enableListenerCollection:false,
126
127         /**
128          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
129          * the IE insecure content warning (defaults to javascript:false).
130          * @type String
131          */
132         SSL_SECURE_URL : "javascript:false",
133
134         /**
135          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
136          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
137          * @type String
138          */
139         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
140
141         emptyFn : function(){},
142         
143         /**
144          * Copies all the properties of config to obj if they don't already exist.
145          * @param {Object} obj The receiver of the properties
146          * @param {Object} config The source of the properties
147          * @return {Object} returns obj
148          */
149         applyIf : function(o, c){
150             if(o && c){
151                 for(var p in c){
152                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
153                 }
154             }
155             return o;
156         },
157
158         /**
159          * Applies event listeners to elements by selectors when the document is ready.
160          * The event name is specified with an @ suffix.
161 <pre><code>
162 Roo.addBehaviors({
163    // add a listener for click on all anchors in element with id foo
164    '#foo a@click' : function(e, t){
165        // do something
166    },
167
168    // add the same listener to multiple selectors (separated by comma BEFORE the @)
169    '#foo a, #bar span.some-class@mouseover' : function(){
170        // do something
171    }
172 });
173 </code></pre>
174          * @param {Object} obj The list of behaviors to apply
175          */
176         addBehaviors : function(o){
177             if(!Roo.isReady){
178                 Roo.onReady(function(){
179                     Roo.addBehaviors(o);
180                 });
181                 return;
182             }
183             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
184             for(var b in o){
185                 var parts = b.split('@');
186                 if(parts[1]){ // for Object prototype breakers
187                     var s = parts[0];
188                     if(!cache[s]){
189                         cache[s] = Roo.select(s);
190                     }
191                     cache[s].on(parts[1], o[b]);
192                 }
193             }
194             cache = null;
195         },
196
197         /**
198          * Generates unique ids. If the element already has an id, it is unchanged
199          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
200          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
201          * @return {String} The generated Id.
202          */
203         id : function(el, prefix){
204             prefix = prefix || "roo-gen";
205             el = Roo.getDom(el);
206             var id = prefix + (++idSeed);
207             return el ? (el.id ? el.id : (el.id = id)) : id;
208         },
209          
210        
211         /**
212          * Extends one class with another class and optionally overrides members with the passed literal. This class
213          * also adds the function "override()" to the class that can be used to override
214          * members on an instance.
215          * @param {Object} subclass The class inheriting the functionality
216          * @param {Object} superclass The class being extended
217          * @param {Object} overrides (optional) A literal with members
218          * @method extend
219          */
220         extend : function(){
221             // inline overrides
222             var io = function(o){
223                 for(var m in o){
224                     this[m] = o[m];
225                 }
226             };
227             return function(sb, sp, overrides){
228                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
229                     overrides = sp;
230                     sp = sb;
231                     sb = function(){sp.apply(this, arguments);};
232                 }
233                 var F = function(){}, sbp, spp = sp.prototype;
234                 F.prototype = spp;
235                 sbp = sb.prototype = new F();
236                 sbp.constructor=sb;
237                 sb.superclass=spp;
238                 
239                 if(spp.constructor == Object.prototype.constructor){
240                     spp.constructor=sp;
241                    
242                 }
243                 
244                 sb.override = function(o){
245                     Roo.override(sb, o);
246                 };
247                 sbp.override = io;
248                 Roo.override(sb, overrides);
249                 return sb;
250             };
251         }(),
252
253         /**
254          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
255          * Usage:<pre><code>
256 Roo.override(MyClass, {
257     newMethod1: function(){
258         // etc.
259     },
260     newMethod2: function(foo){
261         // etc.
262     }
263 });
264  </code></pre>
265          * @param {Object} origclass The class to override
266          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
267          * containing one or more methods.
268          * @method override
269          */
270         override : function(origclass, overrides){
271             if(overrides){
272                 var p = origclass.prototype;
273                 for(var method in overrides){
274                     p[method] = overrides[method];
275                 }
276             }
277         },
278         /**
279          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
280          * <pre><code>
281 Roo.namespace('Company', 'Company.data');
282 Company.Widget = function() { ... }
283 Company.data.CustomStore = function(config) { ... }
284 </code></pre>
285          * @param {String} namespace1
286          * @param {String} namespace2
287          * @param {String} etc
288          * @method namespace
289          */
290         namespace : function(){
291             var a=arguments, o=null, i, j, d, rt;
292             for (i=0; i<a.length; ++i) {
293                 d=a[i].split(".");
294                 rt = d[0];
295                 /** eval:var:o */
296                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
297                 for (j=1; j<d.length; ++j) {
298                     o[d[j]]=o[d[j]] || {};
299                     o=o[d[j]];
300                 }
301             }
302         },
303         /**
304          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
305          * <pre><code>
306 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
307 Roo.factory(conf, Roo.data);
308 </code></pre>
309          * @param {String} classname
310          * @param {String} namespace (optional)
311          * @method factory
312          */
313          
314         factory : function(c, ns)
315         {
316             // no xtype, no ns or c.xns - or forced off by c.xns
317             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
318                 return c;
319             }
320             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
321             if (c.constructor == ns[c.xtype]) {// already created...
322                 return c;
323             }
324             if (ns[c.xtype]) {
325                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
326                 var ret = new ns[c.xtype](c);
327                 ret.xns = false;
328                 return ret;
329             }
330             c.xns = false; // prevent recursion..
331             return c;
332         },
333          /**
334          * Logs to console if it can.
335          *
336          * @param {String|Object} string
337          * @method log
338          */
339         log : function(s)
340         {
341             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
342                 return; // alerT?
343             }
344             console.log(s);
345             
346         },
347         /**
348          * 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.
349          * @param {Object} o
350          * @return {String}
351          */
352         urlEncode : function(o){
353             if(!o){
354                 return "";
355             }
356             var buf = [];
357             for(var key in o){
358                 var ov = o[key], k = Roo.encodeURIComponent(key);
359                 var type = typeof ov;
360                 if(type == 'undefined'){
361                     buf.push(k, "=&");
362                 }else if(type != "function" && type != "object"){
363                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
364                 }else if(ov instanceof Array){
365                     if (ov.length) {
366                             for(var i = 0, len = ov.length; i < len; i++) {
367                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
368                             }
369                         } else {
370                             buf.push(k, "=&");
371                         }
372                 }
373             }
374             buf.pop();
375             return buf.join("");
376         },
377          /**
378          * Safe version of encodeURIComponent
379          * @param {String} data 
380          * @return {String} 
381          */
382         
383         encodeURIComponent : function (data)
384         {
385             try {
386                 return encodeURIComponent(data);
387             } catch(e) {} // should be an uri encode error.
388             
389             if (data == '' || data == null){
390                return '';
391             }
392             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
393             function nibble_to_hex(nibble){
394                 var chars = '0123456789ABCDEF';
395                 return chars.charAt(nibble);
396             }
397             data = data.toString();
398             var buffer = '';
399             for(var i=0; i<data.length; i++){
400                 var c = data.charCodeAt(i);
401                 var bs = new Array();
402                 if (c > 0x10000){
403                         // 4 bytes
404                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
405                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
406                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
407                     bs[3] = 0x80 | (c & 0x3F);
408                 }else if (c > 0x800){
409                          // 3 bytes
410                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
411                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
412                     bs[2] = 0x80 | (c & 0x3F);
413                 }else if (c > 0x80){
414                        // 2 bytes
415                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
416                     bs[1] = 0x80 | (c & 0x3F);
417                 }else{
418                         // 1 byte
419                     bs[0] = c;
420                 }
421                 for(var j=0; j<bs.length; j++){
422                     var b = bs[j];
423                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
424                             + nibble_to_hex(b &0x0F);
425                     buffer += '%'+hex;
426                }
427             }
428             return buffer;    
429              
430         },
431
432         /**
433          * 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]}.
434          * @param {String} string
435          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
436          * @return {Object} A literal with members
437          */
438         urlDecode : function(string, overwrite){
439             if(!string || !string.length){
440                 return {};
441             }
442             var obj = {};
443             var pairs = string.split('&');
444             var pair, name, value;
445             for(var i = 0, len = pairs.length; i < len; i++){
446                 pair = pairs[i].split('=');
447                 name = decodeURIComponent(pair[0]);
448                 value = decodeURIComponent(pair[1]);
449                 if(overwrite !== true){
450                     if(typeof obj[name] == "undefined"){
451                         obj[name] = value;
452                     }else if(typeof obj[name] == "string"){
453                         obj[name] = [obj[name]];
454                         obj[name].push(value);
455                     }else{
456                         obj[name].push(value);
457                     }
458                 }else{
459                     obj[name] = value;
460                 }
461             }
462             return obj;
463         },
464
465         /**
466          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
467          * passed array is not really an array, your function is called once with it.
468          * The supplied function is called with (Object item, Number index, Array allItems).
469          * @param {Array/NodeList/Mixed} array
470          * @param {Function} fn
471          * @param {Object} scope
472          */
473         each : function(array, fn, scope){
474             if(typeof array.length == "undefined" || typeof array == "string"){
475                 array = [array];
476             }
477             for(var i = 0, len = array.length; i < len; i++){
478                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
479             }
480         },
481
482         // deprecated
483         combine : function(){
484             var as = arguments, l = as.length, r = [];
485             for(var i = 0; i < l; i++){
486                 var a = as[i];
487                 if(a instanceof Array){
488                     r = r.concat(a);
489                 }else if(a.length !== undefined && !a.substr){
490                     r = r.concat(Array.prototype.slice.call(a, 0));
491                 }else{
492                     r.push(a);
493                 }
494             }
495             return r;
496         },
497
498         /**
499          * Escapes the passed string for use in a regular expression
500          * @param {String} str
501          * @return {String}
502          */
503         escapeRe : function(s) {
504             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
505         },
506
507         // internal
508         callback : function(cb, scope, args, delay){
509             if(typeof cb == "function"){
510                 if(delay){
511                     cb.defer(delay, scope, args || []);
512                 }else{
513                     cb.apply(scope, args || []);
514                 }
515             }
516         },
517
518         /**
519          * Return the dom node for the passed string (id), dom node, or Roo.Element
520          * @param {String/HTMLElement/Roo.Element} el
521          * @return HTMLElement
522          */
523         getDom : function(el){
524             if(!el){
525                 return null;
526             }
527             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
528         },
529
530         /**
531         * Shorthand for {@link Roo.ComponentMgr#get}
532         * @param {String} id
533         * @return Roo.Component
534         */
535         getCmp : function(id){
536             return Roo.ComponentMgr.get(id);
537         },
538          
539         num : function(v, defaultValue){
540             if(typeof v != 'number'){
541                 return defaultValue;
542             }
543             return v;
544         },
545
546         destroy : function(){
547             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
548                 var as = a[i];
549                 if(as){
550                     if(as.dom){
551                         as.removeAllListeners();
552                         as.remove();
553                         continue;
554                     }
555                     if(typeof as.purgeListeners == 'function'){
556                         as.purgeListeners();
557                     }
558                     if(typeof as.destroy == 'function'){
559                         as.destroy();
560                     }
561                 }
562             }
563         },
564
565         // inpired by a similar function in mootools library
566         /**
567          * Returns the type of object that is passed in. If the object passed in is null or undefined it
568          * return false otherwise it returns one of the following values:<ul>
569          * <li><b>string</b>: If the object passed is a string</li>
570          * <li><b>number</b>: If the object passed is a number</li>
571          * <li><b>boolean</b>: If the object passed is a boolean value</li>
572          * <li><b>function</b>: If the object passed is a function reference</li>
573          * <li><b>object</b>: If the object passed is an object</li>
574          * <li><b>array</b>: If the object passed is an array</li>
575          * <li><b>regexp</b>: If the object passed is a regular expression</li>
576          * <li><b>element</b>: If the object passed is a DOM Element</li>
577          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
578          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
579          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
580          * @param {Mixed} object
581          * @return {String}
582          */
583         type : function(o){
584             if(o === undefined || o === null){
585                 return false;
586             }
587             if(o.htmlElement){
588                 return 'element';
589             }
590             var t = typeof o;
591             if(t == 'object' && o.nodeName) {
592                 switch(o.nodeType) {
593                     case 1: return 'element';
594                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
595                 }
596             }
597             if(t == 'object' || t == 'function') {
598                 switch(o.constructor) {
599                     case Array: return 'array';
600                     case RegExp: return 'regexp';
601                 }
602                 if(typeof o.length == 'number' && typeof o.item == 'function') {
603                     return 'nodelist';
604                 }
605             }
606             return t;
607         },
608
609         /**
610          * Returns true if the passed value is null, undefined or an empty string (optional).
611          * @param {Mixed} value The value to test
612          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
613          * @return {Boolean}
614          */
615         isEmpty : function(v, allowBlank){
616             return v === null || v === undefined || (!allowBlank ? v === '' : false);
617         },
618         
619         /** @type Boolean */
620         isOpera : isOpera,
621         /** @type Boolean */
622         isSafari : isSafari,
623         /** @type Boolean */
624         isFirefox : isFirefox,
625         /** @type Boolean */
626         isIE : isIE,
627         /** @type Boolean */
628         isIE7 : isIE7,
629         /** @type Boolean */
630         isIE11 : isIE11,
631         /** @type Boolean */
632         isGecko : isGecko,
633         /** @type Boolean */
634         isBorderBox : isBorderBox,
635         /** @type Boolean */
636         isWindows : isWindows,
637         /** @type Boolean */
638         isLinux : isLinux,
639         /** @type Boolean */
640         isMac : isMac,
641         /** @type Boolean */
642         isIOS : isIOS,
643         /** @type Boolean */
644         isAndroid : isAndroid,
645         /** @type Boolean */
646         isTouch : isTouch,
647
648         /**
649          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
650          * you may want to set this to true.
651          * @type Boolean
652          */
653         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
654         
655         
656                 
657         /**
658          * Selects a single element as a Roo Element
659          * This is about as close as you can get to jQuery's $('do crazy stuff')
660          * @param {String} selector The selector/xpath query
661          * @param {Node} root (optional) The start of the query (defaults to document).
662          * @return {Roo.Element}
663          */
664         selectNode : function(selector, root) 
665         {
666             var node = Roo.DomQuery.selectNode(selector,root);
667             return node ? Roo.get(node) : new Roo.Element(false);
668         }
669         
670     });
671
672
673 })();
674
675 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
676                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
677                 "Roo.app", "Roo.ux",
678                 "Roo.bootstrap",
679                 "Roo.bootstrap.dash");
680 /*
681  * Based on:
682  * Ext JS Library 1.1.1
683  * Copyright(c) 2006-2007, Ext JS, LLC.
684  *
685  * Originally Released Under LGPL - original licence link has changed is not relivant.
686  *
687  * Fork - LGPL
688  * <script type="text/javascript">
689  */
690
691 (function() {    
692     // wrappedn so fnCleanup is not in global scope...
693     if(Roo.isIE) {
694         function fnCleanUp() {
695             var p = Function.prototype;
696             delete p.createSequence;
697             delete p.defer;
698             delete p.createDelegate;
699             delete p.createCallback;
700             delete p.createInterceptor;
701
702             window.detachEvent("onunload", fnCleanUp);
703         }
704         window.attachEvent("onunload", fnCleanUp);
705     }
706 })();
707
708
709 /**
710  * @class Function
711  * These functions are available on every Function object (any JavaScript function).
712  */
713 Roo.apply(Function.prototype, {
714      /**
715      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
716      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
717      * Will create a function that is bound to those 2 args.
718      * @return {Function} The new function
719     */
720     createCallback : function(/*args...*/){
721         // make args available, in function below
722         var args = arguments;
723         var method = this;
724         return function() {
725             return method.apply(window, args);
726         };
727     },
728
729     /**
730      * Creates a delegate (callback) that sets the scope to obj.
731      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
732      * Will create a function that is automatically scoped to this.
733      * @param {Object} obj (optional) The object for which the scope is set
734      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
735      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
736      *                                             if a number the args are inserted at the specified position
737      * @return {Function} The new function
738      */
739     createDelegate : function(obj, args, appendArgs){
740         var method = this;
741         return function() {
742             var callArgs = args || arguments;
743             if(appendArgs === true){
744                 callArgs = Array.prototype.slice.call(arguments, 0);
745                 callArgs = callArgs.concat(args);
746             }else if(typeof appendArgs == "number"){
747                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
748                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
749                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
750             }
751             return method.apply(obj || window, callArgs);
752         };
753     },
754
755     /**
756      * Calls this function after the number of millseconds specified.
757      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
758      * @param {Object} obj (optional) The object for which the scope is set
759      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
760      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
761      *                                             if a number the args are inserted at the specified position
762      * @return {Number} The timeout id that can be used with clearTimeout
763      */
764     defer : function(millis, obj, args, appendArgs){
765         var fn = this.createDelegate(obj, args, appendArgs);
766         if(millis){
767             return setTimeout(fn, millis);
768         }
769         fn();
770         return 0;
771     },
772     /**
773      * Create a combined function call sequence of the original function + the passed function.
774      * The resulting function returns the results of the original function.
775      * The passed fcn is called with the parameters of the original function
776      * @param {Function} fcn The function to sequence
777      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
778      * @return {Function} The new function
779      */
780     createSequence : function(fcn, scope){
781         if(typeof fcn != "function"){
782             return this;
783         }
784         var method = this;
785         return function() {
786             var retval = method.apply(this || window, arguments);
787             fcn.apply(scope || this || window, arguments);
788             return retval;
789         };
790     },
791
792     /**
793      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
794      * The resulting function returns the results of the original function.
795      * The passed fcn is called with the parameters of the original function.
796      * @addon
797      * @param {Function} fcn The function to call before the original
798      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
799      * @return {Function} The new function
800      */
801     createInterceptor : function(fcn, scope){
802         if(typeof fcn != "function"){
803             return this;
804         }
805         var method = this;
806         return function() {
807             fcn.target = this;
808             fcn.method = method;
809             if(fcn.apply(scope || this || window, arguments) === false){
810                 return;
811             }
812             return method.apply(this || window, arguments);
813         };
814     }
815 });
816 /*
817  * Based on:
818  * Ext JS Library 1.1.1
819  * Copyright(c) 2006-2007, Ext JS, LLC.
820  *
821  * Originally Released Under LGPL - original licence link has changed is not relivant.
822  *
823  * Fork - LGPL
824  * <script type="text/javascript">
825  */
826
827 Roo.applyIf(String, {
828     
829     /** @scope String */
830     
831     /**
832      * Escapes the passed string for ' and \
833      * @param {String} string The string to escape
834      * @return {String} The escaped string
835      * @static
836      */
837     escape : function(string) {
838         return string.replace(/('|\\)/g, "\\$1");
839     },
840
841     /**
842      * Pads the left side of a string with a specified character.  This is especially useful
843      * for normalizing number and date strings.  Example usage:
844      * <pre><code>
845 var s = String.leftPad('123', 5, '0');
846 // s now contains the string: '00123'
847 </code></pre>
848      * @param {String} string The original string
849      * @param {Number} size The total length of the output string
850      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
851      * @return {String} The padded string
852      * @static
853      */
854     leftPad : function (val, size, ch) {
855         var result = new String(val);
856         if(ch === null || ch === undefined || ch === '') {
857             ch = " ";
858         }
859         while (result.length < size) {
860             result = ch + result;
861         }
862         return result;
863     },
864
865     /**
866      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
867      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
868      * <pre><code>
869 var cls = 'my-class', text = 'Some text';
870 var s = String.format('<div class="{0}">{1}</div>', cls, text);
871 // s now contains the string: '<div class="my-class">Some text</div>'
872 </code></pre>
873      * @param {String} string The tokenized string to be formatted
874      * @param {String} value1 The value to replace token {0}
875      * @param {String} value2 Etc...
876      * @return {String} The formatted string
877      * @static
878      */
879     format : function(format){
880         var args = Array.prototype.slice.call(arguments, 1);
881         return format.replace(/\{(\d+)\}/g, function(m, i){
882             return Roo.util.Format.htmlEncode(args[i]);
883         });
884     }
885 });
886
887 /**
888  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
889  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
890  * they are already different, the first value passed in is returned.  Note that this method returns the new value
891  * but does not change the current string.
892  * <pre><code>
893 // alternate sort directions
894 sort = sort.toggle('ASC', 'DESC');
895
896 // instead of conditional logic:
897 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
898 </code></pre>
899  * @param {String} value The value to compare to the current string
900  * @param {String} other The new value to use if the string already equals the first value passed in
901  * @return {String} The new value
902  */
903  
904 String.prototype.toggle = function(value, other){
905     return this == value ? other : value;
906 };/*
907  * Based on:
908  * Ext JS Library 1.1.1
909  * Copyright(c) 2006-2007, Ext JS, LLC.
910  *
911  * Originally Released Under LGPL - original licence link has changed is not relivant.
912  *
913  * Fork - LGPL
914  * <script type="text/javascript">
915  */
916
917  /**
918  * @class Number
919  */
920 Roo.applyIf(Number.prototype, {
921     /**
922      * Checks whether or not the current number is within a desired range.  If the number is already within the
923      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
924      * exceeded.  Note that this method returns the constrained value but does not change the current number.
925      * @param {Number} min The minimum number in the range
926      * @param {Number} max The maximum number in the range
927      * @return {Number} The constrained value if outside the range, otherwise the current value
928      */
929     constrain : function(min, max){
930         return Math.min(Math.max(this, min), max);
931     }
932 });/*
933  * Based on:
934  * Ext JS Library 1.1.1
935  * Copyright(c) 2006-2007, Ext JS, LLC.
936  *
937  * Originally Released Under LGPL - original licence link has changed is not relivant.
938  *
939  * Fork - LGPL
940  * <script type="text/javascript">
941  */
942  /**
943  * @class Array
944  */
945 Roo.applyIf(Array.prototype, {
946     /**
947      * 
948      * Checks whether or not the specified object exists in the array.
949      * @param {Object} o The object to check for
950      * @return {Number} The index of o in the array (or -1 if it is not found)
951      */
952     indexOf : function(o){
953        for (var i = 0, len = this.length; i < len; i++){
954               if(this[i] == o) { return i; }
955        }
956            return -1;
957     },
958
959     /**
960      * Removes the specified object from the array.  If the object is not found nothing happens.
961      * @param {Object} o The object to remove
962      */
963     remove : function(o){
964        var index = this.indexOf(o);
965        if(index != -1){
966            this.splice(index, 1);
967        }
968     },
969     /**
970      * Map (JS 1.6 compatibility)
971      * @param {Function} function  to call
972      */
973     map : function(fun )
974     {
975         var len = this.length >>> 0;
976         if (typeof fun != "function") {
977             throw new TypeError();
978         }
979         var res = new Array(len);
980         var thisp = arguments[1];
981         for (var i = 0; i < len; i++)
982         {
983             if (i in this) {
984                 res[i] = fun.call(thisp, this[i], i, this);
985             }
986         }
987
988         return res;
989     }
990     
991 });
992
993
994  
995 /*
996  * Based on:
997  * Ext JS Library 1.1.1
998  * Copyright(c) 2006-2007, Ext JS, LLC.
999  *
1000  * Originally Released Under LGPL - original licence link has changed is not relivant.
1001  *
1002  * Fork - LGPL
1003  * <script type="text/javascript">
1004  */
1005
1006 /**
1007  * @class Date
1008  *
1009  * The date parsing and format syntax is a subset of
1010  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1011  * supported will provide results equivalent to their PHP versions.
1012  *
1013  * Following is the list of all currently supported formats:
1014  *<pre>
1015 Sample date:
1016 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1017
1018 Format  Output      Description
1019 ------  ----------  --------------------------------------------------------------
1020   d      10         Day of the month, 2 digits with leading zeros
1021   D      Wed        A textual representation of a day, three letters
1022   j      10         Day of the month without leading zeros
1023   l      Wednesday  A full textual representation of the day of the week
1024   S      th         English ordinal day of month suffix, 2 chars (use with j)
1025   w      3          Numeric representation of the day of the week
1026   z      9          The julian date, or day of the year (0-365)
1027   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1028   F      January    A full textual representation of the month
1029   m      01         Numeric representation of a month, with leading zeros
1030   M      Jan        Month name abbreviation, three letters
1031   n      1          Numeric representation of a month, without leading zeros
1032   t      31         Number of days in the given month
1033   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1034   Y      2007       A full numeric representation of a year, 4 digits
1035   y      07         A two digit representation of a year
1036   a      pm         Lowercase Ante meridiem and Post meridiem
1037   A      PM         Uppercase Ante meridiem and Post meridiem
1038   g      3          12-hour format of an hour without leading zeros
1039   G      15         24-hour format of an hour without leading zeros
1040   h      03         12-hour format of an hour with leading zeros
1041   H      15         24-hour format of an hour with leading zeros
1042   i      05         Minutes with leading zeros
1043   s      01         Seconds, with leading zeros
1044   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1045   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1046   T      CST        Timezone setting of the machine running the code
1047   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1048 </pre>
1049  *
1050  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1051  * <pre><code>
1052 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1053 document.write(dt.format('Y-m-d'));                         //2007-01-10
1054 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1055 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
1056  </code></pre>
1057  *
1058  * Here are some standard date/time patterns that you might find helpful.  They
1059  * are not part of the source of Date.js, but to use them you can simply copy this
1060  * block of code into any script that is included after Date.js and they will also become
1061  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1062  * <pre><code>
1063 Date.patterns = {
1064     ISO8601Long:"Y-m-d H:i:s",
1065     ISO8601Short:"Y-m-d",
1066     ShortDate: "n/j/Y",
1067     LongDate: "l, F d, Y",
1068     FullDateTime: "l, F d, Y g:i:s A",
1069     MonthDay: "F d",
1070     ShortTime: "g:i A",
1071     LongTime: "g:i:s A",
1072     SortableDateTime: "Y-m-d\\TH:i:s",
1073     UniversalSortableDateTime: "Y-m-d H:i:sO",
1074     YearMonth: "F, Y"
1075 };
1076 </code></pre>
1077  *
1078  * Example usage:
1079  * <pre><code>
1080 var dt = new Date();
1081 document.write(dt.format(Date.patterns.ShortDate));
1082  </code></pre>
1083  */
1084
1085 /*
1086  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1087  * They generate precompiled functions from date formats instead of parsing and
1088  * processing the pattern every time you format a date.  These functions are available
1089  * on every Date object (any javascript function).
1090  *
1091  * The original article and download are here:
1092  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1093  *
1094  */
1095  
1096  
1097  // was in core
1098 /**
1099  Returns the number of milliseconds between this date and date
1100  @param {Date} date (optional) Defaults to now
1101  @return {Number} The diff in milliseconds
1102  @member Date getElapsed
1103  */
1104 Date.prototype.getElapsed = function(date) {
1105         return Math.abs((date || new Date()).getTime()-this.getTime());
1106 };
1107 // was in date file..
1108
1109
1110 // private
1111 Date.parseFunctions = {count:0};
1112 // private
1113 Date.parseRegexes = [];
1114 // private
1115 Date.formatFunctions = {count:0};
1116
1117 // private
1118 Date.prototype.dateFormat = function(format) {
1119     if (Date.formatFunctions[format] == null) {
1120         Date.createNewFormat(format);
1121     }
1122     var func = Date.formatFunctions[format];
1123     return this[func]();
1124 };
1125
1126
1127 /**
1128  * Formats a date given the supplied format string
1129  * @param {String} format The format string
1130  * @return {String} The formatted date
1131  * @method
1132  */
1133 Date.prototype.format = Date.prototype.dateFormat;
1134
1135 // private
1136 Date.createNewFormat = function(format) {
1137     var funcName = "format" + Date.formatFunctions.count++;
1138     Date.formatFunctions[format] = funcName;
1139     var code = "Date.prototype." + funcName + " = function(){return ";
1140     var special = false;
1141     var ch = '';
1142     for (var i = 0; i < format.length; ++i) {
1143         ch = format.charAt(i);
1144         if (!special && ch == "\\") {
1145             special = true;
1146         }
1147         else if (special) {
1148             special = false;
1149             code += "'" + String.escape(ch) + "' + ";
1150         }
1151         else {
1152             code += Date.getFormatCode(ch);
1153         }
1154     }
1155     /** eval:var:zzzzzzzzzzzzz */
1156     eval(code.substring(0, code.length - 3) + ";}");
1157 };
1158
1159 // private
1160 Date.getFormatCode = function(character) {
1161     switch (character) {
1162     case "d":
1163         return "String.leftPad(this.getDate(), 2, '0') + ";
1164     case "D":
1165         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1166     case "j":
1167         return "this.getDate() + ";
1168     case "l":
1169         return "Date.dayNames[this.getDay()] + ";
1170     case "S":
1171         return "this.getSuffix() + ";
1172     case "w":
1173         return "this.getDay() + ";
1174     case "z":
1175         return "this.getDayOfYear() + ";
1176     case "W":
1177         return "this.getWeekOfYear() + ";
1178     case "F":
1179         return "Date.monthNames[this.getMonth()] + ";
1180     case "m":
1181         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1182     case "M":
1183         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1184     case "n":
1185         return "(this.getMonth() + 1) + ";
1186     case "t":
1187         return "this.getDaysInMonth() + ";
1188     case "L":
1189         return "(this.isLeapYear() ? 1 : 0) + ";
1190     case "Y":
1191         return "this.getFullYear() + ";
1192     case "y":
1193         return "('' + this.getFullYear()).substring(2, 4) + ";
1194     case "a":
1195         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1196     case "A":
1197         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1198     case "g":
1199         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1200     case "G":
1201         return "this.getHours() + ";
1202     case "h":
1203         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1204     case "H":
1205         return "String.leftPad(this.getHours(), 2, '0') + ";
1206     case "i":
1207         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1208     case "s":
1209         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1210     case "O":
1211         return "this.getGMTOffset() + ";
1212     case "P":
1213         return "this.getGMTColonOffset() + ";
1214     case "T":
1215         return "this.getTimezone() + ";
1216     case "Z":
1217         return "(this.getTimezoneOffset() * -60) + ";
1218     default:
1219         return "'" + String.escape(character) + "' + ";
1220     }
1221 };
1222
1223 /**
1224  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1225  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1226  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1227  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1228  * string or the parse operation will fail.
1229  * Example Usage:
1230 <pre><code>
1231 //dt = Fri May 25 2007 (current date)
1232 var dt = new Date();
1233
1234 //dt = Thu May 25 2006 (today's month/day in 2006)
1235 dt = Date.parseDate("2006", "Y");
1236
1237 //dt = Sun Jan 15 2006 (all date parts specified)
1238 dt = Date.parseDate("2006-1-15", "Y-m-d");
1239
1240 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1241 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1242 </code></pre>
1243  * @param {String} input The unparsed date as a string
1244  * @param {String} format The format the date is in
1245  * @return {Date} The parsed date
1246  * @static
1247  */
1248 Date.parseDate = function(input, format) {
1249     if (Date.parseFunctions[format] == null) {
1250         Date.createParser(format);
1251     }
1252     var func = Date.parseFunctions[format];
1253     return Date[func](input);
1254 };
1255 /**
1256  * @private
1257  */
1258
1259 Date.createParser = function(format) {
1260     var funcName = "parse" + Date.parseFunctions.count++;
1261     var regexNum = Date.parseRegexes.length;
1262     var currentGroup = 1;
1263     Date.parseFunctions[format] = funcName;
1264
1265     var code = "Date." + funcName + " = function(input){\n"
1266         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1267         + "var d = new Date();\n"
1268         + "y = d.getFullYear();\n"
1269         + "m = d.getMonth();\n"
1270         + "d = d.getDate();\n"
1271         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1272         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1273         + "if (results && results.length > 0) {";
1274     var regex = "";
1275
1276     var special = false;
1277     var ch = '';
1278     for (var i = 0; i < format.length; ++i) {
1279         ch = format.charAt(i);
1280         if (!special && ch == "\\") {
1281             special = true;
1282         }
1283         else if (special) {
1284             special = false;
1285             regex += String.escape(ch);
1286         }
1287         else {
1288             var obj = Date.formatCodeToRegex(ch, currentGroup);
1289             currentGroup += obj.g;
1290             regex += obj.s;
1291             if (obj.g && obj.c) {
1292                 code += obj.c;
1293             }
1294         }
1295     }
1296
1297     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1298         + "{v = new Date(y, m, d, h, i, s);}\n"
1299         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1300         + "{v = new Date(y, m, d, h, i);}\n"
1301         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1302         + "{v = new Date(y, m, d, h);}\n"
1303         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1304         + "{v = new Date(y, m, d);}\n"
1305         + "else if (y >= 0 && m >= 0)\n"
1306         + "{v = new Date(y, m);}\n"
1307         + "else if (y >= 0)\n"
1308         + "{v = new Date(y);}\n"
1309         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1310         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1311         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1312         + ";}";
1313
1314     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1315     /** eval:var:zzzzzzzzzzzzz */
1316     eval(code);
1317 };
1318
1319 // private
1320 Date.formatCodeToRegex = function(character, currentGroup) {
1321     switch (character) {
1322     case "D":
1323         return {g:0,
1324         c:null,
1325         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1326     case "j":
1327         return {g:1,
1328             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1329             s:"(\\d{1,2})"}; // day of month without leading zeroes
1330     case "d":
1331         return {g:1,
1332             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1333             s:"(\\d{2})"}; // day of month with leading zeroes
1334     case "l":
1335         return {g:0,
1336             c:null,
1337             s:"(?:" + Date.dayNames.join("|") + ")"};
1338     case "S":
1339         return {g:0,
1340             c:null,
1341             s:"(?:st|nd|rd|th)"};
1342     case "w":
1343         return {g:0,
1344             c:null,
1345             s:"\\d"};
1346     case "z":
1347         return {g:0,
1348             c:null,
1349             s:"(?:\\d{1,3})"};
1350     case "W":
1351         return {g:0,
1352             c:null,
1353             s:"(?:\\d{2})"};
1354     case "F":
1355         return {g:1,
1356             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1357             s:"(" + Date.monthNames.join("|") + ")"};
1358     case "M":
1359         return {g:1,
1360             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1361             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1362     case "n":
1363         return {g:1,
1364             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1365             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1366     case "m":
1367         return {g:1,
1368             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1369             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1370     case "t":
1371         return {g:0,
1372             c:null,
1373             s:"\\d{1,2}"};
1374     case "L":
1375         return {g:0,
1376             c:null,
1377             s:"(?:1|0)"};
1378     case "Y":
1379         return {g:1,
1380             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1381             s:"(\\d{4})"};
1382     case "y":
1383         return {g:1,
1384             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1385                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1386             s:"(\\d{1,2})"};
1387     case "a":
1388         return {g:1,
1389             c:"if (results[" + currentGroup + "] == 'am') {\n"
1390                 + "if (h == 12) { h = 0; }\n"
1391                 + "} else { if (h < 12) { h += 12; }}",
1392             s:"(am|pm)"};
1393     case "A":
1394         return {g:1,
1395             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1396                 + "if (h == 12) { h = 0; }\n"
1397                 + "} else { if (h < 12) { h += 12; }}",
1398             s:"(AM|PM)"};
1399     case "g":
1400     case "G":
1401         return {g:1,
1402             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1403             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1404     case "h":
1405     case "H":
1406         return {g:1,
1407             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1408             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1409     case "i":
1410         return {g:1,
1411             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1412             s:"(\\d{2})"};
1413     case "s":
1414         return {g:1,
1415             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1416             s:"(\\d{2})"};
1417     case "O":
1418         return {g:1,
1419             c:[
1420                 "o = results[", currentGroup, "];\n",
1421                 "var sn = o.substring(0,1);\n", // get + / - sign
1422                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1423                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1424                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1425                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1426             ].join(""),
1427             s:"([+\-]\\d{2,4})"};
1428     
1429     
1430     case "P":
1431         return {g:1,
1432                 c:[
1433                    "o = results[", currentGroup, "];\n",
1434                    "var sn = o.substring(0,1);\n",
1435                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1436                    "var mn = o.substring(4,6) % 60;\n",
1437                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1438                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1439             ].join(""),
1440             s:"([+\-]\\d{4})"};
1441     case "T":
1442         return {g:0,
1443             c:null,
1444             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1445     case "Z":
1446         return {g:1,
1447             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1448                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1449             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1450     default:
1451         return {g:0,
1452             c:null,
1453             s:String.escape(character)};
1454     }
1455 };
1456
1457 /**
1458  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1459  * @return {String} The abbreviated timezone name (e.g. 'CST')
1460  */
1461 Date.prototype.getTimezone = function() {
1462     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1463 };
1464
1465 /**
1466  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1467  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1468  */
1469 Date.prototype.getGMTOffset = function() {
1470     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1471         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1472         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1473 };
1474
1475 /**
1476  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1477  * @return {String} 2-characters representing hours and 2-characters representing minutes
1478  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1479  */
1480 Date.prototype.getGMTColonOffset = function() {
1481         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1482                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1483                 + ":"
1484                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1485 }
1486
1487 /**
1488  * Get the numeric day number of the year, adjusted for leap year.
1489  * @return {Number} 0 through 364 (365 in leap years)
1490  */
1491 Date.prototype.getDayOfYear = function() {
1492     var num = 0;
1493     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1494     for (var i = 0; i < this.getMonth(); ++i) {
1495         num += Date.daysInMonth[i];
1496     }
1497     return num + this.getDate() - 1;
1498 };
1499
1500 /**
1501  * Get the string representation of the numeric week number of the year
1502  * (equivalent to the format specifier 'W').
1503  * @return {String} '00' through '52'
1504  */
1505 Date.prototype.getWeekOfYear = function() {
1506     // Skip to Thursday of this week
1507     var now = this.getDayOfYear() + (4 - this.getDay());
1508     // Find the first Thursday of the year
1509     var jan1 = new Date(this.getFullYear(), 0, 1);
1510     var then = (7 - jan1.getDay() + 4);
1511     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1512 };
1513
1514 /**
1515  * Whether or not the current date is in a leap year.
1516  * @return {Boolean} True if the current date is in a leap year, else false
1517  */
1518 Date.prototype.isLeapYear = function() {
1519     var year = this.getFullYear();
1520     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1521 };
1522
1523 /**
1524  * Get the first day of the current month, adjusted for leap year.  The returned value
1525  * is the numeric day index within the week (0-6) which can be used in conjunction with
1526  * the {@link #monthNames} array to retrieve the textual day name.
1527  * Example:
1528  *<pre><code>
1529 var dt = new Date('1/10/2007');
1530 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1531 </code></pre>
1532  * @return {Number} The day number (0-6)
1533  */
1534 Date.prototype.getFirstDayOfMonth = function() {
1535     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1536     return (day < 0) ? (day + 7) : day;
1537 };
1538
1539 /**
1540  * Get the last day of the current month, adjusted for leap year.  The returned value
1541  * is the numeric day index within the week (0-6) which can be used in conjunction with
1542  * the {@link #monthNames} array to retrieve the textual day name.
1543  * Example:
1544  *<pre><code>
1545 var dt = new Date('1/10/2007');
1546 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1547 </code></pre>
1548  * @return {Number} The day number (0-6)
1549  */
1550 Date.prototype.getLastDayOfMonth = function() {
1551     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1552     return (day < 0) ? (day + 7) : day;
1553 };
1554
1555
1556 /**
1557  * Get the first date of this date's month
1558  * @return {Date}
1559  */
1560 Date.prototype.getFirstDateOfMonth = function() {
1561     return new Date(this.getFullYear(), this.getMonth(), 1);
1562 };
1563
1564 /**
1565  * Get the last date of this date's month
1566  * @return {Date}
1567  */
1568 Date.prototype.getLastDateOfMonth = function() {
1569     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1570 };
1571 /**
1572  * Get the number of days in the current month, adjusted for leap year.
1573  * @return {Number} The number of days in the month
1574  */
1575 Date.prototype.getDaysInMonth = function() {
1576     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1577     return Date.daysInMonth[this.getMonth()];
1578 };
1579
1580 /**
1581  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1582  * @return {String} 'st, 'nd', 'rd' or 'th'
1583  */
1584 Date.prototype.getSuffix = function() {
1585     switch (this.getDate()) {
1586         case 1:
1587         case 21:
1588         case 31:
1589             return "st";
1590         case 2:
1591         case 22:
1592             return "nd";
1593         case 3:
1594         case 23:
1595             return "rd";
1596         default:
1597             return "th";
1598     }
1599 };
1600
1601 // private
1602 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1603
1604 /**
1605  * An array of textual month names.
1606  * Override these values for international dates, for example...
1607  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1608  * @type Array
1609  * @static
1610  */
1611 Date.monthNames =
1612    ["January",
1613     "February",
1614     "March",
1615     "April",
1616     "May",
1617     "June",
1618     "July",
1619     "August",
1620     "September",
1621     "October",
1622     "November",
1623     "December"];
1624
1625 /**
1626  * An array of textual day names.
1627  * Override these values for international dates, for example...
1628  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1629  * @type Array
1630  * @static
1631  */
1632 Date.dayNames =
1633    ["Sunday",
1634     "Monday",
1635     "Tuesday",
1636     "Wednesday",
1637     "Thursday",
1638     "Friday",
1639     "Saturday"];
1640
1641 // private
1642 Date.y2kYear = 50;
1643 // private
1644 Date.monthNumbers = {
1645     Jan:0,
1646     Feb:1,
1647     Mar:2,
1648     Apr:3,
1649     May:4,
1650     Jun:5,
1651     Jul:6,
1652     Aug:7,
1653     Sep:8,
1654     Oct:9,
1655     Nov:10,
1656     Dec:11};
1657
1658 /**
1659  * Creates and returns a new Date instance with the exact same date value as the called instance.
1660  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1661  * variable will also be changed.  When the intention is to create a new variable that will not
1662  * modify the original instance, you should create a clone.
1663  *
1664  * Example of correctly cloning a date:
1665  * <pre><code>
1666 //wrong way:
1667 var orig = new Date('10/1/2006');
1668 var copy = orig;
1669 copy.setDate(5);
1670 document.write(orig);  //returns 'Thu Oct 05 2006'!
1671
1672 //correct way:
1673 var orig = new Date('10/1/2006');
1674 var copy = orig.clone();
1675 copy.setDate(5);
1676 document.write(orig);  //returns 'Thu Oct 01 2006'
1677 </code></pre>
1678  * @return {Date} The new Date instance
1679  */
1680 Date.prototype.clone = function() {
1681         return new Date(this.getTime());
1682 };
1683
1684 /**
1685  * Clears any time information from this date
1686  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1687  @return {Date} this or the clone
1688  */
1689 Date.prototype.clearTime = function(clone){
1690     if(clone){
1691         return this.clone().clearTime();
1692     }
1693     this.setHours(0);
1694     this.setMinutes(0);
1695     this.setSeconds(0);
1696     this.setMilliseconds(0);
1697     return this;
1698 };
1699
1700 // private
1701 // safari setMonth is broken -- check that this is only donw once...
1702 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1703     Date.brokenSetMonth = Date.prototype.setMonth;
1704         Date.prototype.setMonth = function(num){
1705                 if(num <= -1){
1706                         var n = Math.ceil(-num);
1707                         var back_year = Math.ceil(n/12);
1708                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1709                         this.setFullYear(this.getFullYear() - back_year);
1710                         return Date.brokenSetMonth.call(this, month);
1711                 } else {
1712                         return Date.brokenSetMonth.apply(this, arguments);
1713                 }
1714         };
1715 }
1716
1717 /** Date interval constant 
1718 * @static 
1719 * @type String */
1720 Date.MILLI = "ms";
1721 /** Date interval constant 
1722 * @static 
1723 * @type String */
1724 Date.SECOND = "s";
1725 /** Date interval constant 
1726 * @static 
1727 * @type String */
1728 Date.MINUTE = "mi";
1729 /** Date interval constant 
1730 * @static 
1731 * @type String */
1732 Date.HOUR = "h";
1733 /** Date interval constant 
1734 * @static 
1735 * @type String */
1736 Date.DAY = "d";
1737 /** Date interval constant 
1738 * @static 
1739 * @type String */
1740 Date.MONTH = "mo";
1741 /** Date interval constant 
1742 * @static 
1743 * @type String */
1744 Date.YEAR = "y";
1745
1746 /**
1747  * Provides a convenient method of performing basic date arithmetic.  This method
1748  * does not modify the Date instance being called - it creates and returns
1749  * a new Date instance containing the resulting date value.
1750  *
1751  * Examples:
1752  * <pre><code>
1753 //Basic usage:
1754 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1755 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1756
1757 //Negative values will subtract correctly:
1758 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1759 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1760
1761 //You can even chain several calls together in one line!
1762 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1763 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1764  </code></pre>
1765  *
1766  * @param {String} interval   A valid date interval enum value
1767  * @param {Number} value      The amount to add to the current date
1768  * @return {Date} The new Date instance
1769  */
1770 Date.prototype.add = function(interval, value){
1771   var d = this.clone();
1772   if (!interval || value === 0) { return d; }
1773   switch(interval.toLowerCase()){
1774     case Date.MILLI:
1775       d.setMilliseconds(this.getMilliseconds() + value);
1776       break;
1777     case Date.SECOND:
1778       d.setSeconds(this.getSeconds() + value);
1779       break;
1780     case Date.MINUTE:
1781       d.setMinutes(this.getMinutes() + value);
1782       break;
1783     case Date.HOUR:
1784       d.setHours(this.getHours() + value);
1785       break;
1786     case Date.DAY:
1787       d.setDate(this.getDate() + value);
1788       break;
1789     case Date.MONTH:
1790       var day = this.getDate();
1791       if(day > 28){
1792           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1793       }
1794       d.setDate(day);
1795       d.setMonth(this.getMonth() + value);
1796       break;
1797     case Date.YEAR:
1798       d.setFullYear(this.getFullYear() + value);
1799       break;
1800   }
1801   return d;
1802 };
1803 /*
1804  * Based on:
1805  * Ext JS Library 1.1.1
1806  * Copyright(c) 2006-2007, Ext JS, LLC.
1807  *
1808  * Originally Released Under LGPL - original licence link has changed is not relivant.
1809  *
1810  * Fork - LGPL
1811  * <script type="text/javascript">
1812  */
1813
1814 /**
1815  * @class Roo.lib.Dom
1816  * @static
1817  * 
1818  * Dom utils (from YIU afaik)
1819  * 
1820  **/
1821 Roo.lib.Dom = {
1822     /**
1823      * Get the view width
1824      * @param {Boolean} full True will get the full document, otherwise it's the view width
1825      * @return {Number} The width
1826      */
1827      
1828     getViewWidth : function(full) {
1829         return full ? this.getDocumentWidth() : this.getViewportWidth();
1830     },
1831     /**
1832      * Get the view height
1833      * @param {Boolean} full True will get the full document, otherwise it's the view height
1834      * @return {Number} The height
1835      */
1836     getViewHeight : function(full) {
1837         return full ? this.getDocumentHeight() : this.getViewportHeight();
1838     },
1839
1840     getDocumentHeight: function() {
1841         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1842         return Math.max(scrollHeight, this.getViewportHeight());
1843     },
1844
1845     getDocumentWidth: function() {
1846         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1847         return Math.max(scrollWidth, this.getViewportWidth());
1848     },
1849
1850     getViewportHeight: function() {
1851         var height = self.innerHeight;
1852         var mode = document.compatMode;
1853
1854         if ((mode || Roo.isIE) && !Roo.isOpera) {
1855             height = (mode == "CSS1Compat") ?
1856                      document.documentElement.clientHeight :
1857                      document.body.clientHeight;
1858         }
1859
1860         return height;
1861     },
1862
1863     getViewportWidth: function() {
1864         var width = self.innerWidth;
1865         var mode = document.compatMode;
1866
1867         if (mode || Roo.isIE) {
1868             width = (mode == "CSS1Compat") ?
1869                     document.documentElement.clientWidth :
1870                     document.body.clientWidth;
1871         }
1872         return width;
1873     },
1874
1875     isAncestor : function(p, c) {
1876         p = Roo.getDom(p);
1877         c = Roo.getDom(c);
1878         if (!p || !c) {
1879             return false;
1880         }
1881
1882         if (p.contains && !Roo.isSafari) {
1883             return p.contains(c);
1884         } else if (p.compareDocumentPosition) {
1885             return !!(p.compareDocumentPosition(c) & 16);
1886         } else {
1887             var parent = c.parentNode;
1888             while (parent) {
1889                 if (parent == p) {
1890                     return true;
1891                 }
1892                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1893                     return false;
1894                 }
1895                 parent = parent.parentNode;
1896             }
1897             return false;
1898         }
1899     },
1900
1901     getRegion : function(el) {
1902         return Roo.lib.Region.getRegion(el);
1903     },
1904
1905     getY : function(el) {
1906         return this.getXY(el)[1];
1907     },
1908
1909     getX : function(el) {
1910         return this.getXY(el)[0];
1911     },
1912
1913     getXY : function(el) {
1914         var p, pe, b, scroll, bd = document.body;
1915         el = Roo.getDom(el);
1916         var fly = Roo.lib.AnimBase.fly;
1917         if (el.getBoundingClientRect) {
1918             b = el.getBoundingClientRect();
1919             scroll = fly(document).getScroll();
1920             return [b.left + scroll.left, b.top + scroll.top];
1921         }
1922         var x = 0, y = 0;
1923
1924         p = el;
1925
1926         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1927
1928         while (p) {
1929
1930             x += p.offsetLeft;
1931             y += p.offsetTop;
1932
1933             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1934                 hasAbsolute = true;
1935             }
1936
1937             if (Roo.isGecko) {
1938                 pe = fly(p);
1939
1940                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1941                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1942
1943
1944                 x += bl;
1945                 y += bt;
1946
1947
1948                 if (p != el && pe.getStyle('overflow') != 'visible') {
1949                     x += bl;
1950                     y += bt;
1951                 }
1952             }
1953             p = p.offsetParent;
1954         }
1955
1956         if (Roo.isSafari && hasAbsolute) {
1957             x -= bd.offsetLeft;
1958             y -= bd.offsetTop;
1959         }
1960
1961         if (Roo.isGecko && !hasAbsolute) {
1962             var dbd = fly(bd);
1963             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1964             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1965         }
1966
1967         p = el.parentNode;
1968         while (p && p != bd) {
1969             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1970                 x -= p.scrollLeft;
1971                 y -= p.scrollTop;
1972             }
1973             p = p.parentNode;
1974         }
1975         return [x, y];
1976     },
1977  
1978   
1979
1980
1981     setXY : function(el, xy) {
1982         el = Roo.fly(el, '_setXY');
1983         el.position();
1984         var pts = el.translatePoints(xy);
1985         if (xy[0] !== false) {
1986             el.dom.style.left = pts.left + "px";
1987         }
1988         if (xy[1] !== false) {
1989             el.dom.style.top = pts.top + "px";
1990         }
1991     },
1992
1993     setX : function(el, x) {
1994         this.setXY(el, [x, false]);
1995     },
1996
1997     setY : function(el, y) {
1998         this.setXY(el, [false, y]);
1999     }
2000 };
2001 /*
2002  * Portions of this file are based on pieces of Yahoo User Interface Library
2003  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2004  * YUI licensed under the BSD License:
2005  * http://developer.yahoo.net/yui/license.txt
2006  * <script type="text/javascript">
2007  *
2008  */
2009
2010 Roo.lib.Event = function() {
2011     var loadComplete = false;
2012     var listeners = [];
2013     var unloadListeners = [];
2014     var retryCount = 0;
2015     var onAvailStack = [];
2016     var counter = 0;
2017     var lastError = null;
2018
2019     return {
2020         POLL_RETRYS: 200,
2021         POLL_INTERVAL: 20,
2022         EL: 0,
2023         TYPE: 1,
2024         FN: 2,
2025         WFN: 3,
2026         OBJ: 3,
2027         ADJ_SCOPE: 4,
2028         _interval: null,
2029
2030         startInterval: function() {
2031             if (!this._interval) {
2032                 var self = this;
2033                 var callback = function() {
2034                     self._tryPreloadAttach();
2035                 };
2036                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2037
2038             }
2039         },
2040
2041         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2042             onAvailStack.push({ id:         p_id,
2043                 fn:         p_fn,
2044                 obj:        p_obj,
2045                 override:   p_override,
2046                 checkReady: false    });
2047
2048             retryCount = this.POLL_RETRYS;
2049             this.startInterval();
2050         },
2051
2052
2053         addListener: function(el, eventName, fn) {
2054             el = Roo.getDom(el);
2055             if (!el || !fn) {
2056                 return false;
2057             }
2058
2059             if ("unload" == eventName) {
2060                 unloadListeners[unloadListeners.length] =
2061                 [el, eventName, fn];
2062                 return true;
2063             }
2064
2065             var wrappedFn = function(e) {
2066                 return fn(Roo.lib.Event.getEvent(e));
2067             };
2068
2069             var li = [el, eventName, fn, wrappedFn];
2070
2071             var index = listeners.length;
2072             listeners[index] = li;
2073
2074             this.doAdd(el, eventName, wrappedFn, false);
2075             return true;
2076
2077         },
2078
2079
2080         removeListener: function(el, eventName, fn) {
2081             var i, len;
2082
2083             el = Roo.getDom(el);
2084
2085             if(!fn) {
2086                 return this.purgeElement(el, false, eventName);
2087             }
2088
2089
2090             if ("unload" == eventName) {
2091
2092                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2093                     var li = unloadListeners[i];
2094                     if (li &&
2095                         li[0] == el &&
2096                         li[1] == eventName &&
2097                         li[2] == fn) {
2098                         unloadListeners.splice(i, 1);
2099                         return true;
2100                     }
2101                 }
2102
2103                 return false;
2104             }
2105
2106             var cacheItem = null;
2107
2108
2109             var index = arguments[3];
2110
2111             if ("undefined" == typeof index) {
2112                 index = this._getCacheIndex(el, eventName, fn);
2113             }
2114
2115             if (index >= 0) {
2116                 cacheItem = listeners[index];
2117             }
2118
2119             if (!el || !cacheItem) {
2120                 return false;
2121             }
2122
2123             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2124
2125             delete listeners[index][this.WFN];
2126             delete listeners[index][this.FN];
2127             listeners.splice(index, 1);
2128
2129             return true;
2130
2131         },
2132
2133
2134         getTarget: function(ev, resolveTextNode) {
2135             ev = ev.browserEvent || ev;
2136             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2137             var t = ev.target || ev.srcElement;
2138             return this.resolveTextNode(t);
2139         },
2140
2141
2142         resolveTextNode: function(node) {
2143             if (Roo.isSafari && node && 3 == node.nodeType) {
2144                 return node.parentNode;
2145             } else {
2146                 return node;
2147             }
2148         },
2149
2150
2151         getPageX: function(ev) {
2152             ev = ev.browserEvent || ev;
2153             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2154             var x = ev.pageX;
2155             if (!x && 0 !== x) {
2156                 x = ev.clientX || 0;
2157
2158                 if (Roo.isIE) {
2159                     x += this.getScroll()[1];
2160                 }
2161             }
2162
2163             return x;
2164         },
2165
2166
2167         getPageY: function(ev) {
2168             ev = ev.browserEvent || ev;
2169             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2170             var y = ev.pageY;
2171             if (!y && 0 !== y) {
2172                 y = ev.clientY || 0;
2173
2174                 if (Roo.isIE) {
2175                     y += this.getScroll()[0];
2176                 }
2177             }
2178
2179
2180             return y;
2181         },
2182
2183
2184         getXY: function(ev) {
2185             ev = ev.browserEvent || ev;
2186             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2187             return [this.getPageX(ev), this.getPageY(ev)];
2188         },
2189
2190
2191         getRelatedTarget: function(ev) {
2192             ev = ev.browserEvent || ev;
2193             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2194             var t = ev.relatedTarget;
2195             if (!t) {
2196                 if (ev.type == "mouseout") {
2197                     t = ev.toElement;
2198                 } else if (ev.type == "mouseover") {
2199                     t = ev.fromElement;
2200                 }
2201             }
2202
2203             return this.resolveTextNode(t);
2204         },
2205
2206
2207         getTime: function(ev) {
2208             ev = ev.browserEvent || ev;
2209             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2210             if (!ev.time) {
2211                 var t = new Date().getTime();
2212                 try {
2213                     ev.time = t;
2214                 } catch(ex) {
2215                     this.lastError = ex;
2216                     return t;
2217                 }
2218             }
2219
2220             return ev.time;
2221         },
2222
2223
2224         stopEvent: function(ev) {
2225             this.stopPropagation(ev);
2226             this.preventDefault(ev);
2227         },
2228
2229
2230         stopPropagation: function(ev) {
2231             ev = ev.browserEvent || ev;
2232             if (ev.stopPropagation) {
2233                 ev.stopPropagation();
2234             } else {
2235                 ev.cancelBubble = true;
2236             }
2237         },
2238
2239
2240         preventDefault: function(ev) {
2241             ev = ev.browserEvent || ev;
2242             if(ev.preventDefault) {
2243                 ev.preventDefault();
2244             } else {
2245                 ev.returnValue = false;
2246             }
2247         },
2248
2249
2250         getEvent: function(e) {
2251             var ev = e || window.event;
2252             if (!ev) {
2253                 var c = this.getEvent.caller;
2254                 while (c) {
2255                     ev = c.arguments[0];
2256                     if (ev && Event == ev.constructor) {
2257                         break;
2258                     }
2259                     c = c.caller;
2260                 }
2261             }
2262             return ev;
2263         },
2264
2265
2266         getCharCode: function(ev) {
2267             ev = ev.browserEvent || ev;
2268             return ev.charCode || ev.keyCode || 0;
2269         },
2270
2271
2272         _getCacheIndex: function(el, eventName, fn) {
2273             for (var i = 0,len = listeners.length; i < len; ++i) {
2274                 var li = listeners[i];
2275                 if (li &&
2276                     li[this.FN] == fn &&
2277                     li[this.EL] == el &&
2278                     li[this.TYPE] == eventName) {
2279                     return i;
2280                 }
2281             }
2282
2283             return -1;
2284         },
2285
2286
2287         elCache: {},
2288
2289
2290         getEl: function(id) {
2291             return document.getElementById(id);
2292         },
2293
2294
2295         clearCache: function() {
2296         },
2297
2298
2299         _load: function(e) {
2300             loadComplete = true;
2301             var EU = Roo.lib.Event;
2302
2303
2304             if (Roo.isIE) {
2305                 EU.doRemove(window, "load", EU._load);
2306             }
2307         },
2308
2309
2310         _tryPreloadAttach: function() {
2311
2312             if (this.locked) {
2313                 return false;
2314             }
2315
2316             this.locked = true;
2317
2318
2319             var tryAgain = !loadComplete;
2320             if (!tryAgain) {
2321                 tryAgain = (retryCount > 0);
2322             }
2323
2324
2325             var notAvail = [];
2326             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2327                 var item = onAvailStack[i];
2328                 if (item) {
2329                     var el = this.getEl(item.id);
2330
2331                     if (el) {
2332                         if (!item.checkReady ||
2333                             loadComplete ||
2334                             el.nextSibling ||
2335                             (document && document.body)) {
2336
2337                             var scope = el;
2338                             if (item.override) {
2339                                 if (item.override === true) {
2340                                     scope = item.obj;
2341                                 } else {
2342                                     scope = item.override;
2343                                 }
2344                             }
2345                             item.fn.call(scope, item.obj);
2346                             onAvailStack[i] = null;
2347                         }
2348                     } else {
2349                         notAvail.push(item);
2350                     }
2351                 }
2352             }
2353
2354             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2355
2356             if (tryAgain) {
2357
2358                 this.startInterval();
2359             } else {
2360                 clearInterval(this._interval);
2361                 this._interval = null;
2362             }
2363
2364             this.locked = false;
2365
2366             return true;
2367
2368         },
2369
2370
2371         purgeElement: function(el, recurse, eventName) {
2372             var elListeners = this.getListeners(el, eventName);
2373             if (elListeners) {
2374                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2375                     var l = elListeners[i];
2376                     this.removeListener(el, l.type, l.fn);
2377                 }
2378             }
2379
2380             if (recurse && el && el.childNodes) {
2381                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2382                     this.purgeElement(el.childNodes[i], recurse, eventName);
2383                 }
2384             }
2385         },
2386
2387
2388         getListeners: function(el, eventName) {
2389             var results = [], searchLists;
2390             if (!eventName) {
2391                 searchLists = [listeners, unloadListeners];
2392             } else if (eventName == "unload") {
2393                 searchLists = [unloadListeners];
2394             } else {
2395                 searchLists = [listeners];
2396             }
2397
2398             for (var j = 0; j < searchLists.length; ++j) {
2399                 var searchList = searchLists[j];
2400                 if (searchList && searchList.length > 0) {
2401                     for (var i = 0,len = searchList.length; i < len; ++i) {
2402                         var l = searchList[i];
2403                         if (l && l[this.EL] === el &&
2404                             (!eventName || eventName === l[this.TYPE])) {
2405                             results.push({
2406                                 type:   l[this.TYPE],
2407                                 fn:     l[this.FN],
2408                                 obj:    l[this.OBJ],
2409                                 adjust: l[this.ADJ_SCOPE],
2410                                 index:  i
2411                             });
2412                         }
2413                     }
2414                 }
2415             }
2416
2417             return (results.length) ? results : null;
2418         },
2419
2420
2421         _unload: function(e) {
2422
2423             var EU = Roo.lib.Event, i, j, l, len, index;
2424
2425             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2426                 l = unloadListeners[i];
2427                 if (l) {
2428                     var scope = window;
2429                     if (l[EU.ADJ_SCOPE]) {
2430                         if (l[EU.ADJ_SCOPE] === true) {
2431                             scope = l[EU.OBJ];
2432                         } else {
2433                             scope = l[EU.ADJ_SCOPE];
2434                         }
2435                     }
2436                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2437                     unloadListeners[i] = null;
2438                     l = null;
2439                     scope = null;
2440                 }
2441             }
2442
2443             unloadListeners = null;
2444
2445             if (listeners && listeners.length > 0) {
2446                 j = listeners.length;
2447                 while (j) {
2448                     index = j - 1;
2449                     l = listeners[index];
2450                     if (l) {
2451                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2452                                 l[EU.FN], index);
2453                     }
2454                     j = j - 1;
2455                 }
2456                 l = null;
2457
2458                 EU.clearCache();
2459             }
2460
2461             EU.doRemove(window, "unload", EU._unload);
2462
2463         },
2464
2465
2466         getScroll: function() {
2467             var dd = document.documentElement, db = document.body;
2468             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2469                 return [dd.scrollTop, dd.scrollLeft];
2470             } else if (db) {
2471                 return [db.scrollTop, db.scrollLeft];
2472             } else {
2473                 return [0, 0];
2474             }
2475         },
2476
2477
2478         doAdd: function () {
2479             if (window.addEventListener) {
2480                 return function(el, eventName, fn, capture) {
2481                     el.addEventListener(eventName, fn, (capture));
2482                 };
2483             } else if (window.attachEvent) {
2484                 return function(el, eventName, fn, capture) {
2485                     el.attachEvent("on" + eventName, fn);
2486                 };
2487             } else {
2488                 return function() {
2489                 };
2490             }
2491         }(),
2492
2493
2494         doRemove: function() {
2495             if (window.removeEventListener) {
2496                 return function (el, eventName, fn, capture) {
2497                     el.removeEventListener(eventName, fn, (capture));
2498                 };
2499             } else if (window.detachEvent) {
2500                 return function (el, eventName, fn) {
2501                     el.detachEvent("on" + eventName, fn);
2502                 };
2503             } else {
2504                 return function() {
2505                 };
2506             }
2507         }()
2508     };
2509     
2510 }();
2511 (function() {     
2512    
2513     var E = Roo.lib.Event;
2514     E.on = E.addListener;
2515     E.un = E.removeListener;
2516
2517     if (document && document.body) {
2518         E._load();
2519     } else {
2520         E.doAdd(window, "load", E._load);
2521     }
2522     E.doAdd(window, "unload", E._unload);
2523     E._tryPreloadAttach();
2524 })();
2525
2526 /*
2527  * Portions of this file are based on pieces of Yahoo User Interface Library
2528  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2529  * YUI licensed under the BSD License:
2530  * http://developer.yahoo.net/yui/license.txt
2531  * <script type="text/javascript">
2532  *
2533  */
2534
2535 (function() {
2536     /**
2537      * @class Roo.lib.Ajax
2538      *
2539      */
2540     Roo.lib.Ajax = {
2541         /**
2542          * @static 
2543          */
2544         request : function(method, uri, cb, data, options) {
2545             if(options){
2546                 var hs = options.headers;
2547                 if(hs){
2548                     for(var h in hs){
2549                         if(hs.hasOwnProperty(h)){
2550                             this.initHeader(h, hs[h], false);
2551                         }
2552                     }
2553                 }
2554                 if(options.xmlData){
2555                     this.initHeader('Content-Type', 'text/xml', false);
2556                     method = 'POST';
2557                     data = options.xmlData;
2558                 }
2559             }
2560
2561             return this.asyncRequest(method, uri, cb, data);
2562         },
2563
2564         serializeForm : function(form) {
2565             if(typeof form == 'string') {
2566                 form = (document.getElementById(form) || document.forms[form]);
2567             }
2568
2569             var el, name, val, disabled, data = '', hasSubmit = false;
2570             for (var i = 0; i < form.elements.length; i++) {
2571                 el = form.elements[i];
2572                 disabled = form.elements[i].disabled;
2573                 name = form.elements[i].name;
2574                 val = form.elements[i].value;
2575
2576                 if (!disabled && name){
2577                     switch (el.type)
2578                             {
2579                         case 'select-one':
2580                         case 'select-multiple':
2581                             for (var j = 0; j < el.options.length; j++) {
2582                                 if (el.options[j].selected) {
2583                                     if (Roo.isIE) {
2584                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2585                                     }
2586                                     else {
2587                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2588                                     }
2589                                 }
2590                             }
2591                             break;
2592                         case 'radio':
2593                         case 'checkbox':
2594                             if (el.checked) {
2595                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2596                             }
2597                             break;
2598                         case 'file':
2599
2600                         case undefined:
2601
2602                         case 'reset':
2603
2604                         case 'button':
2605
2606                             break;
2607                         case 'submit':
2608                             if(hasSubmit == false) {
2609                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2610                                 hasSubmit = true;
2611                             }
2612                             break;
2613                         default:
2614                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2615                             break;
2616                     }
2617                 }
2618             }
2619             data = data.substr(0, data.length - 1);
2620             return data;
2621         },
2622
2623         headers:{},
2624
2625         hasHeaders:false,
2626
2627         useDefaultHeader:true,
2628
2629         defaultPostHeader:'application/x-www-form-urlencoded',
2630
2631         useDefaultXhrHeader:true,
2632
2633         defaultXhrHeader:'XMLHttpRequest',
2634
2635         hasDefaultHeaders:true,
2636
2637         defaultHeaders:{},
2638
2639         poll:{},
2640
2641         timeout:{},
2642
2643         pollInterval:50,
2644
2645         transactionId:0,
2646
2647         setProgId:function(id)
2648         {
2649             this.activeX.unshift(id);
2650         },
2651
2652         setDefaultPostHeader:function(b)
2653         {
2654             this.useDefaultHeader = b;
2655         },
2656
2657         setDefaultXhrHeader:function(b)
2658         {
2659             this.useDefaultXhrHeader = b;
2660         },
2661
2662         setPollingInterval:function(i)
2663         {
2664             if (typeof i == 'number' && isFinite(i)) {
2665                 this.pollInterval = i;
2666             }
2667         },
2668
2669         createXhrObject:function(transactionId)
2670         {
2671             var obj,http;
2672             try
2673             {
2674
2675                 http = new XMLHttpRequest();
2676
2677                 obj = { conn:http, tId:transactionId };
2678             }
2679             catch(e)
2680             {
2681                 for (var i = 0; i < this.activeX.length; ++i) {
2682                     try
2683                     {
2684
2685                         http = new ActiveXObject(this.activeX[i]);
2686
2687                         obj = { conn:http, tId:transactionId };
2688                         break;
2689                     }
2690                     catch(e) {
2691                     }
2692                 }
2693             }
2694             finally
2695             {
2696                 return obj;
2697             }
2698         },
2699
2700         getConnectionObject:function()
2701         {
2702             var o;
2703             var tId = this.transactionId;
2704
2705             try
2706             {
2707                 o = this.createXhrObject(tId);
2708                 if (o) {
2709                     this.transactionId++;
2710                 }
2711             }
2712             catch(e) {
2713             }
2714             finally
2715             {
2716                 return o;
2717             }
2718         },
2719
2720         asyncRequest:function(method, uri, callback, postData)
2721         {
2722             var o = this.getConnectionObject();
2723
2724             if (!o) {
2725                 return null;
2726             }
2727             else {
2728                 o.conn.open(method, uri, true);
2729
2730                 if (this.useDefaultXhrHeader) {
2731                     if (!this.defaultHeaders['X-Requested-With']) {
2732                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2733                     }
2734                 }
2735
2736                 if(postData && this.useDefaultHeader){
2737                     this.initHeader('Content-Type', this.defaultPostHeader);
2738                 }
2739
2740                  if (this.hasDefaultHeaders || this.hasHeaders) {
2741                     this.setHeader(o);
2742                 }
2743
2744                 this.handleReadyState(o, callback);
2745                 o.conn.send(postData || null);
2746
2747                 return o;
2748             }
2749         },
2750
2751         handleReadyState:function(o, callback)
2752         {
2753             var oConn = this;
2754
2755             if (callback && callback.timeout) {
2756                 
2757                 this.timeout[o.tId] = window.setTimeout(function() {
2758                     oConn.abort(o, callback, true);
2759                 }, callback.timeout);
2760             }
2761
2762             this.poll[o.tId] = window.setInterval(
2763                     function() {
2764                         if (o.conn && o.conn.readyState == 4) {
2765                             window.clearInterval(oConn.poll[o.tId]);
2766                             delete oConn.poll[o.tId];
2767
2768                             if(callback && callback.timeout) {
2769                                 window.clearTimeout(oConn.timeout[o.tId]);
2770                                 delete oConn.timeout[o.tId];
2771                             }
2772
2773                             oConn.handleTransactionResponse(o, callback);
2774                         }
2775                     }
2776                     , this.pollInterval);
2777         },
2778
2779         handleTransactionResponse:function(o, callback, isAbort)
2780         {
2781
2782             if (!callback) {
2783                 this.releaseObject(o);
2784                 return;
2785             }
2786
2787             var httpStatus, responseObject;
2788
2789             try
2790             {
2791                 if (o.conn.status !== undefined && o.conn.status != 0) {
2792                     httpStatus = o.conn.status;
2793                 }
2794                 else {
2795                     httpStatus = 13030;
2796                 }
2797             }
2798             catch(e) {
2799
2800
2801                 httpStatus = 13030;
2802             }
2803
2804             if (httpStatus >= 200 && httpStatus < 300) {
2805                 responseObject = this.createResponseObject(o, callback.argument);
2806                 if (callback.success) {
2807                     if (!callback.scope) {
2808                         callback.success(responseObject);
2809                     }
2810                     else {
2811
2812
2813                         callback.success.apply(callback.scope, [responseObject]);
2814                     }
2815                 }
2816             }
2817             else {
2818                 switch (httpStatus) {
2819
2820                     case 12002:
2821                     case 12029:
2822                     case 12030:
2823                     case 12031:
2824                     case 12152:
2825                     case 13030:
2826                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2827                         if (callback.failure) {
2828                             if (!callback.scope) {
2829                                 callback.failure(responseObject);
2830                             }
2831                             else {
2832                                 callback.failure.apply(callback.scope, [responseObject]);
2833                             }
2834                         }
2835                         break;
2836                     default:
2837                         responseObject = this.createResponseObject(o, callback.argument);
2838                         if (callback.failure) {
2839                             if (!callback.scope) {
2840                                 callback.failure(responseObject);
2841                             }
2842                             else {
2843                                 callback.failure.apply(callback.scope, [responseObject]);
2844                             }
2845                         }
2846                 }
2847             }
2848
2849             this.releaseObject(o);
2850             responseObject = null;
2851         },
2852
2853         createResponseObject:function(o, callbackArg)
2854         {
2855             var obj = {};
2856             var headerObj = {};
2857
2858             try
2859             {
2860                 var headerStr = o.conn.getAllResponseHeaders();
2861                 var header = headerStr.split('\n');
2862                 for (var i = 0; i < header.length; i++) {
2863                     var delimitPos = header[i].indexOf(':');
2864                     if (delimitPos != -1) {
2865                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2866                     }
2867                 }
2868             }
2869             catch(e) {
2870             }
2871
2872             obj.tId = o.tId;
2873             obj.status = o.conn.status;
2874             obj.statusText = o.conn.statusText;
2875             obj.getResponseHeader = headerObj;
2876             obj.getAllResponseHeaders = headerStr;
2877             obj.responseText = o.conn.responseText;
2878             obj.responseXML = o.conn.responseXML;
2879
2880             if (typeof callbackArg !== undefined) {
2881                 obj.argument = callbackArg;
2882             }
2883
2884             return obj;
2885         },
2886
2887         createExceptionObject:function(tId, callbackArg, isAbort)
2888         {
2889             var COMM_CODE = 0;
2890             var COMM_ERROR = 'communication failure';
2891             var ABORT_CODE = -1;
2892             var ABORT_ERROR = 'transaction aborted';
2893
2894             var obj = {};
2895
2896             obj.tId = tId;
2897             if (isAbort) {
2898                 obj.status = ABORT_CODE;
2899                 obj.statusText = ABORT_ERROR;
2900             }
2901             else {
2902                 obj.status = COMM_CODE;
2903                 obj.statusText = COMM_ERROR;
2904             }
2905
2906             if (callbackArg) {
2907                 obj.argument = callbackArg;
2908             }
2909
2910             return obj;
2911         },
2912
2913         initHeader:function(label, value, isDefault)
2914         {
2915             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2916
2917             if (headerObj[label] === undefined) {
2918                 headerObj[label] = value;
2919             }
2920             else {
2921
2922
2923                 headerObj[label] = value + "," + headerObj[label];
2924             }
2925
2926             if (isDefault) {
2927                 this.hasDefaultHeaders = true;
2928             }
2929             else {
2930                 this.hasHeaders = true;
2931             }
2932         },
2933
2934
2935         setHeader:function(o)
2936         {
2937             if (this.hasDefaultHeaders) {
2938                 for (var prop in this.defaultHeaders) {
2939                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2940                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2941                     }
2942                 }
2943             }
2944
2945             if (this.hasHeaders) {
2946                 for (var prop in this.headers) {
2947                     if (this.headers.hasOwnProperty(prop)) {
2948                         o.conn.setRequestHeader(prop, this.headers[prop]);
2949                     }
2950                 }
2951                 this.headers = {};
2952                 this.hasHeaders = false;
2953             }
2954         },
2955
2956         resetDefaultHeaders:function() {
2957             delete this.defaultHeaders;
2958             this.defaultHeaders = {};
2959             this.hasDefaultHeaders = false;
2960         },
2961
2962         abort:function(o, callback, isTimeout)
2963         {
2964             if(this.isCallInProgress(o)) {
2965                 o.conn.abort();
2966                 window.clearInterval(this.poll[o.tId]);
2967                 delete this.poll[o.tId];
2968                 if (isTimeout) {
2969                     delete this.timeout[o.tId];
2970                 }
2971
2972                 this.handleTransactionResponse(o, callback, true);
2973
2974                 return true;
2975             }
2976             else {
2977                 return false;
2978             }
2979         },
2980
2981
2982         isCallInProgress:function(o)
2983         {
2984             if (o && o.conn) {
2985                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2986             }
2987             else {
2988
2989                 return false;
2990             }
2991         },
2992
2993
2994         releaseObject:function(o)
2995         {
2996
2997             o.conn = null;
2998
2999             o = null;
3000         },
3001
3002         activeX:[
3003         'MSXML2.XMLHTTP.3.0',
3004         'MSXML2.XMLHTTP',
3005         'Microsoft.XMLHTTP'
3006         ]
3007
3008
3009     };
3010 })();/*
3011  * Portions of this file are based on pieces of Yahoo User Interface Library
3012  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3013  * YUI licensed under the BSD License:
3014  * http://developer.yahoo.net/yui/license.txt
3015  * <script type="text/javascript">
3016  *
3017  */
3018
3019 Roo.lib.Region = function(t, r, b, l) {
3020     this.top = t;
3021     this[1] = t;
3022     this.right = r;
3023     this.bottom = b;
3024     this.left = l;
3025     this[0] = l;
3026 };
3027
3028
3029 Roo.lib.Region.prototype = {
3030     contains : function(region) {
3031         return ( region.left >= this.left &&
3032                  region.right <= this.right &&
3033                  region.top >= this.top &&
3034                  region.bottom <= this.bottom    );
3035
3036     },
3037
3038     getArea : function() {
3039         return ( (this.bottom - this.top) * (this.right - this.left) );
3040     },
3041
3042     intersect : function(region) {
3043         var t = Math.max(this.top, region.top);
3044         var r = Math.min(this.right, region.right);
3045         var b = Math.min(this.bottom, region.bottom);
3046         var l = Math.max(this.left, region.left);
3047
3048         if (b >= t && r >= l) {
3049             return new Roo.lib.Region(t, r, b, l);
3050         } else {
3051             return null;
3052         }
3053     },
3054     union : function(region) {
3055         var t = Math.min(this.top, region.top);
3056         var r = Math.max(this.right, region.right);
3057         var b = Math.max(this.bottom, region.bottom);
3058         var l = Math.min(this.left, region.left);
3059
3060         return new Roo.lib.Region(t, r, b, l);
3061     },
3062
3063     adjust : function(t, l, b, r) {
3064         this.top += t;
3065         this.left += l;
3066         this.right += r;
3067         this.bottom += b;
3068         return this;
3069     }
3070 };
3071
3072 Roo.lib.Region.getRegion = function(el) {
3073     var p = Roo.lib.Dom.getXY(el);
3074
3075     var t = p[1];
3076     var r = p[0] + el.offsetWidth;
3077     var b = p[1] + el.offsetHeight;
3078     var l = p[0];
3079
3080     return new Roo.lib.Region(t, r, b, l);
3081 };
3082 /*
3083  * Portions of this file are based on pieces of Yahoo User Interface Library
3084  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3085  * YUI licensed under the BSD License:
3086  * http://developer.yahoo.net/yui/license.txt
3087  * <script type="text/javascript">
3088  *
3089  */
3090 //@@dep Roo.lib.Region
3091
3092
3093 Roo.lib.Point = function(x, y) {
3094     if (x instanceof Array) {
3095         y = x[1];
3096         x = x[0];
3097     }
3098     this.x = this.right = this.left = this[0] = x;
3099     this.y = this.top = this.bottom = this[1] = y;
3100 };
3101
3102 Roo.lib.Point.prototype = new Roo.lib.Region();
3103 /*
3104  * Portions of this file are based on pieces of Yahoo User Interface Library
3105  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3106  * YUI licensed under the BSD License:
3107  * http://developer.yahoo.net/yui/license.txt
3108  * <script type="text/javascript">
3109  *
3110  */
3111  
3112 (function() {   
3113
3114     Roo.lib.Anim = {
3115         scroll : function(el, args, duration, easing, cb, scope) {
3116             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3117         },
3118
3119         motion : function(el, args, duration, easing, cb, scope) {
3120             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3121         },
3122
3123         color : function(el, args, duration, easing, cb, scope) {
3124             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3125         },
3126
3127         run : function(el, args, duration, easing, cb, scope, type) {
3128             type = type || Roo.lib.AnimBase;
3129             if (typeof easing == "string") {
3130                 easing = Roo.lib.Easing[easing];
3131             }
3132             var anim = new type(el, args, duration, easing);
3133             anim.animateX(function() {
3134                 Roo.callback(cb, scope);
3135             });
3136             return anim;
3137         }
3138     };
3139 })();/*
3140  * Portions of this file are based on pieces of Yahoo User Interface Library
3141  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3142  * YUI licensed under the BSD License:
3143  * http://developer.yahoo.net/yui/license.txt
3144  * <script type="text/javascript">
3145  *
3146  */
3147
3148 (function() {    
3149     var libFlyweight;
3150     
3151     function fly(el) {
3152         if (!libFlyweight) {
3153             libFlyweight = new Roo.Element.Flyweight();
3154         }
3155         libFlyweight.dom = el;
3156         return libFlyweight;
3157     }
3158
3159     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3160     
3161    
3162     
3163     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3164         if (el) {
3165             this.init(el, attributes, duration, method);
3166         }
3167     };
3168
3169     Roo.lib.AnimBase.fly = fly;
3170     
3171     
3172     
3173     Roo.lib.AnimBase.prototype = {
3174
3175         toString: function() {
3176             var el = this.getEl();
3177             var id = el.id || el.tagName;
3178             return ("Anim " + id);
3179         },
3180
3181         patterns: {
3182             noNegatives:        /width|height|opacity|padding/i,
3183             offsetAttribute:  /^((width|height)|(top|left))$/,
3184             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3185             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3186         },
3187
3188
3189         doMethod: function(attr, start, end) {
3190             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3191         },
3192
3193
3194         setAttribute: function(attr, val, unit) {
3195             if (this.patterns.noNegatives.test(attr)) {
3196                 val = (val > 0) ? val : 0;
3197             }
3198
3199             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3200         },
3201
3202
3203         getAttribute: function(attr) {
3204             var el = this.getEl();
3205             var val = fly(el).getStyle(attr);
3206
3207             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3208                 return parseFloat(val);
3209             }
3210
3211             var a = this.patterns.offsetAttribute.exec(attr) || [];
3212             var pos = !!( a[3] );
3213             var box = !!( a[2] );
3214
3215
3216             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3217                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3218             } else {
3219                 val = 0;
3220             }
3221
3222             return val;
3223         },
3224
3225
3226         getDefaultUnit: function(attr) {
3227             if (this.patterns.defaultUnit.test(attr)) {
3228                 return 'px';
3229             }
3230
3231             return '';
3232         },
3233
3234         animateX : function(callback, scope) {
3235             var f = function() {
3236                 this.onComplete.removeListener(f);
3237                 if (typeof callback == "function") {
3238                     callback.call(scope || this, this);
3239                 }
3240             };
3241             this.onComplete.addListener(f, this);
3242             this.animate();
3243         },
3244
3245
3246         setRuntimeAttribute: function(attr) {
3247             var start;
3248             var end;
3249             var attributes = this.attributes;
3250
3251             this.runtimeAttributes[attr] = {};
3252
3253             var isset = function(prop) {
3254                 return (typeof prop !== 'undefined');
3255             };
3256
3257             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3258                 return false;
3259             }
3260
3261             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3262
3263
3264             if (isset(attributes[attr]['to'])) {
3265                 end = attributes[attr]['to'];
3266             } else if (isset(attributes[attr]['by'])) {
3267                 if (start.constructor == Array) {
3268                     end = [];
3269                     for (var i = 0, len = start.length; i < len; ++i) {
3270                         end[i] = start[i] + attributes[attr]['by'][i];
3271                     }
3272                 } else {
3273                     end = start + attributes[attr]['by'];
3274                 }
3275             }
3276
3277             this.runtimeAttributes[attr].start = start;
3278             this.runtimeAttributes[attr].end = end;
3279
3280
3281             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3282         },
3283
3284
3285         init: function(el, attributes, duration, method) {
3286
3287             var isAnimated = false;
3288
3289
3290             var startTime = null;
3291
3292
3293             var actualFrames = 0;
3294
3295
3296             el = Roo.getDom(el);
3297
3298
3299             this.attributes = attributes || {};
3300
3301
3302             this.duration = duration || 1;
3303
3304
3305             this.method = method || Roo.lib.Easing.easeNone;
3306
3307
3308             this.useSeconds = true;
3309
3310
3311             this.currentFrame = 0;
3312
3313
3314             this.totalFrames = Roo.lib.AnimMgr.fps;
3315
3316
3317             this.getEl = function() {
3318                 return el;
3319             };
3320
3321
3322             this.isAnimated = function() {
3323                 return isAnimated;
3324             };
3325
3326
3327             this.getStartTime = function() {
3328                 return startTime;
3329             };
3330
3331             this.runtimeAttributes = {};
3332
3333
3334             this.animate = function() {
3335                 if (this.isAnimated()) {
3336                     return false;
3337                 }
3338
3339                 this.currentFrame = 0;
3340
3341                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3342
3343                 Roo.lib.AnimMgr.registerElement(this);
3344             };
3345
3346
3347             this.stop = function(finish) {
3348                 if (finish) {
3349                     this.currentFrame = this.totalFrames;
3350                     this._onTween.fire();
3351                 }
3352                 Roo.lib.AnimMgr.stop(this);
3353             };
3354
3355             var onStart = function() {
3356                 this.onStart.fire();
3357
3358                 this.runtimeAttributes = {};
3359                 for (var attr in this.attributes) {
3360                     this.setRuntimeAttribute(attr);
3361                 }
3362
3363                 isAnimated = true;
3364                 actualFrames = 0;
3365                 startTime = new Date();
3366             };
3367
3368
3369             var onTween = function() {
3370                 var data = {
3371                     duration: new Date() - this.getStartTime(),
3372                     currentFrame: this.currentFrame
3373                 };
3374
3375                 data.toString = function() {
3376                     return (
3377                             'duration: ' + data.duration +
3378                             ', currentFrame: ' + data.currentFrame
3379                             );
3380                 };
3381
3382                 this.onTween.fire(data);
3383
3384                 var runtimeAttributes = this.runtimeAttributes;
3385
3386                 for (var attr in runtimeAttributes) {
3387                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3388                 }
3389
3390                 actualFrames += 1;
3391             };
3392
3393             var onComplete = function() {
3394                 var actual_duration = (new Date() - startTime) / 1000 ;
3395
3396                 var data = {
3397                     duration: actual_duration,
3398                     frames: actualFrames,
3399                     fps: actualFrames / actual_duration
3400                 };
3401
3402                 data.toString = function() {
3403                     return (
3404                             'duration: ' + data.duration +
3405                             ', frames: ' + data.frames +
3406                             ', fps: ' + data.fps
3407                             );
3408                 };
3409
3410                 isAnimated = false;
3411                 actualFrames = 0;
3412                 this.onComplete.fire(data);
3413             };
3414
3415
3416             this._onStart = new Roo.util.Event(this);
3417             this.onStart = new Roo.util.Event(this);
3418             this.onTween = new Roo.util.Event(this);
3419             this._onTween = new Roo.util.Event(this);
3420             this.onComplete = new Roo.util.Event(this);
3421             this._onComplete = new Roo.util.Event(this);
3422             this._onStart.addListener(onStart);
3423             this._onTween.addListener(onTween);
3424             this._onComplete.addListener(onComplete);
3425         }
3426     };
3427 })();
3428 /*
3429  * Portions of this file are based on pieces of Yahoo User Interface Library
3430  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3431  * YUI licensed under the BSD License:
3432  * http://developer.yahoo.net/yui/license.txt
3433  * <script type="text/javascript">
3434  *
3435  */
3436
3437 Roo.lib.AnimMgr = new function() {
3438
3439     var thread = null;
3440
3441
3442     var queue = [];
3443
3444
3445     var tweenCount = 0;
3446
3447
3448     this.fps = 1000;
3449
3450
3451     this.delay = 1;
3452
3453
3454     this.registerElement = function(tween) {
3455         queue[queue.length] = tween;
3456         tweenCount += 1;
3457         tween._onStart.fire();
3458         this.start();
3459     };
3460
3461
3462     this.unRegister = function(tween, index) {
3463         tween._onComplete.fire();
3464         index = index || getIndex(tween);
3465         if (index != -1) {
3466             queue.splice(index, 1);
3467         }
3468
3469         tweenCount -= 1;
3470         if (tweenCount <= 0) {
3471             this.stop();
3472         }
3473     };
3474
3475
3476     this.start = function() {
3477         if (thread === null) {
3478             thread = setInterval(this.run, this.delay);
3479         }
3480     };
3481
3482
3483     this.stop = function(tween) {
3484         if (!tween) {
3485             clearInterval(thread);
3486
3487             for (var i = 0, len = queue.length; i < len; ++i) {
3488                 if (queue[0].isAnimated()) {
3489                     this.unRegister(queue[0], 0);
3490                 }
3491             }
3492
3493             queue = [];
3494             thread = null;
3495             tweenCount = 0;
3496         }
3497         else {
3498             this.unRegister(tween);
3499         }
3500     };
3501
3502
3503     this.run = function() {
3504         for (var i = 0, len = queue.length; i < len; ++i) {
3505             var tween = queue[i];
3506             if (!tween || !tween.isAnimated()) {
3507                 continue;
3508             }
3509
3510             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3511             {
3512                 tween.currentFrame += 1;
3513
3514                 if (tween.useSeconds) {
3515                     correctFrame(tween);
3516                 }
3517                 tween._onTween.fire();
3518             }
3519             else {
3520                 Roo.lib.AnimMgr.stop(tween, i);
3521             }
3522         }
3523     };
3524
3525     var getIndex = function(anim) {
3526         for (var i = 0, len = queue.length; i < len; ++i) {
3527             if (queue[i] == anim) {
3528                 return i;
3529             }
3530         }
3531         return -1;
3532     };
3533
3534
3535     var correctFrame = function(tween) {
3536         var frames = tween.totalFrames;
3537         var frame = tween.currentFrame;
3538         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3539         var elapsed = (new Date() - tween.getStartTime());
3540         var tweak = 0;
3541
3542         if (elapsed < tween.duration * 1000) {
3543             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3544         } else {
3545             tweak = frames - (frame + 1);
3546         }
3547         if (tweak > 0 && isFinite(tweak)) {
3548             if (tween.currentFrame + tweak >= frames) {
3549                 tweak = frames - (frame + 1);
3550             }
3551
3552             tween.currentFrame += tweak;
3553         }
3554     };
3555 };
3556
3557     /*
3558  * Portions of this file are based on pieces of Yahoo User Interface Library
3559  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3560  * YUI licensed under the BSD License:
3561  * http://developer.yahoo.net/yui/license.txt
3562  * <script type="text/javascript">
3563  *
3564  */
3565 Roo.lib.Bezier = new function() {
3566
3567         this.getPosition = function(points, t) {
3568             var n = points.length;
3569             var tmp = [];
3570
3571             for (var i = 0; i < n; ++i) {
3572                 tmp[i] = [points[i][0], points[i][1]];
3573             }
3574
3575             for (var j = 1; j < n; ++j) {
3576                 for (i = 0; i < n - j; ++i) {
3577                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3578                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3579                 }
3580             }
3581
3582             return [ tmp[0][0], tmp[0][1] ];
3583
3584         };
3585     };/*
3586  * Portions of this file are based on pieces of Yahoo User Interface Library
3587  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3588  * YUI licensed under the BSD License:
3589  * http://developer.yahoo.net/yui/license.txt
3590  * <script type="text/javascript">
3591  *
3592  */
3593 (function() {
3594
3595     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3596         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3597     };
3598
3599     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3600
3601     var fly = Roo.lib.AnimBase.fly;
3602     var Y = Roo.lib;
3603     var superclass = Y.ColorAnim.superclass;
3604     var proto = Y.ColorAnim.prototype;
3605
3606     proto.toString = function() {
3607         var el = this.getEl();
3608         var id = el.id || el.tagName;
3609         return ("ColorAnim " + id);
3610     };
3611
3612     proto.patterns.color = /color$/i;
3613     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3614     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3615     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3616     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3617
3618
3619     proto.parseColor = function(s) {
3620         if (s.length == 3) {
3621             return s;
3622         }
3623
3624         var c = this.patterns.hex.exec(s);
3625         if (c && c.length == 4) {
3626             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3627         }
3628
3629         c = this.patterns.rgb.exec(s);
3630         if (c && c.length == 4) {
3631             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3632         }
3633
3634         c = this.patterns.hex3.exec(s);
3635         if (c && c.length == 4) {
3636             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3637         }
3638
3639         return null;
3640     };
3641     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3642     proto.getAttribute = function(attr) {
3643         var el = this.getEl();
3644         if (this.patterns.color.test(attr)) {
3645             var val = fly(el).getStyle(attr);
3646
3647             if (this.patterns.transparent.test(val)) {
3648                 var parent = el.parentNode;
3649                 val = fly(parent).getStyle(attr);
3650
3651                 while (parent && this.patterns.transparent.test(val)) {
3652                     parent = parent.parentNode;
3653                     val = fly(parent).getStyle(attr);
3654                     if (parent.tagName.toUpperCase() == 'HTML') {
3655                         val = '#fff';
3656                     }
3657                 }
3658             }
3659         } else {
3660             val = superclass.getAttribute.call(this, attr);
3661         }
3662
3663         return val;
3664     };
3665     proto.getAttribute = function(attr) {
3666         var el = this.getEl();
3667         if (this.patterns.color.test(attr)) {
3668             var val = fly(el).getStyle(attr);
3669
3670             if (this.patterns.transparent.test(val)) {
3671                 var parent = el.parentNode;
3672                 val = fly(parent).getStyle(attr);
3673
3674                 while (parent && this.patterns.transparent.test(val)) {
3675                     parent = parent.parentNode;
3676                     val = fly(parent).getStyle(attr);
3677                     if (parent.tagName.toUpperCase() == 'HTML') {
3678                         val = '#fff';
3679                     }
3680                 }
3681             }
3682         } else {
3683             val = superclass.getAttribute.call(this, attr);
3684         }
3685
3686         return val;
3687     };
3688
3689     proto.doMethod = function(attr, start, end) {
3690         var val;
3691
3692         if (this.patterns.color.test(attr)) {
3693             val = [];
3694             for (var i = 0, len = start.length; i < len; ++i) {
3695                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3696             }
3697
3698             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3699         }
3700         else {
3701             val = superclass.doMethod.call(this, attr, start, end);
3702         }
3703
3704         return val;
3705     };
3706
3707     proto.setRuntimeAttribute = function(attr) {
3708         superclass.setRuntimeAttribute.call(this, attr);
3709
3710         if (this.patterns.color.test(attr)) {
3711             var attributes = this.attributes;
3712             var start = this.parseColor(this.runtimeAttributes[attr].start);
3713             var end = this.parseColor(this.runtimeAttributes[attr].end);
3714
3715             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3716                 end = this.parseColor(attributes[attr].by);
3717
3718                 for (var i = 0, len = start.length; i < len; ++i) {
3719                     end[i] = start[i] + end[i];
3720                 }
3721             }
3722
3723             this.runtimeAttributes[attr].start = start;
3724             this.runtimeAttributes[attr].end = end;
3725         }
3726     };
3727 })();
3728
3729 /*
3730  * Portions of this file are based on pieces of Yahoo User Interface Library
3731  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3732  * YUI licensed under the BSD License:
3733  * http://developer.yahoo.net/yui/license.txt
3734  * <script type="text/javascript">
3735  *
3736  */
3737 Roo.lib.Easing = {
3738
3739
3740     easeNone: function (t, b, c, d) {
3741         return c * t / d + b;
3742     },
3743
3744
3745     easeIn: function (t, b, c, d) {
3746         return c * (t /= d) * t + b;
3747     },
3748
3749
3750     easeOut: function (t, b, c, d) {
3751         return -c * (t /= d) * (t - 2) + b;
3752     },
3753
3754
3755     easeBoth: function (t, b, c, d) {
3756         if ((t /= d / 2) < 1) {
3757             return c / 2 * t * t + b;
3758         }
3759
3760         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3761     },
3762
3763
3764     easeInStrong: function (t, b, c, d) {
3765         return c * (t /= d) * t * t * t + b;
3766     },
3767
3768
3769     easeOutStrong: function (t, b, c, d) {
3770         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3771     },
3772
3773
3774     easeBothStrong: function (t, b, c, d) {
3775         if ((t /= d / 2) < 1) {
3776             return c / 2 * t * t * t * t + b;
3777         }
3778
3779         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3780     },
3781
3782
3783
3784     elasticIn: function (t, b, c, d, a, p) {
3785         if (t == 0) {
3786             return b;
3787         }
3788         if ((t /= d) == 1) {
3789             return b + c;
3790         }
3791         if (!p) {
3792             p = d * .3;
3793         }
3794
3795         if (!a || a < Math.abs(c)) {
3796             a = c;
3797             var s = p / 4;
3798         }
3799         else {
3800             var s = p / (2 * Math.PI) * Math.asin(c / a);
3801         }
3802
3803         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3804     },
3805
3806
3807     elasticOut: function (t, b, c, d, a, p) {
3808         if (t == 0) {
3809             return b;
3810         }
3811         if ((t /= d) == 1) {
3812             return b + c;
3813         }
3814         if (!p) {
3815             p = d * .3;
3816         }
3817
3818         if (!a || a < Math.abs(c)) {
3819             a = c;
3820             var s = p / 4;
3821         }
3822         else {
3823             var s = p / (2 * Math.PI) * Math.asin(c / a);
3824         }
3825
3826         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3827     },
3828
3829
3830     elasticBoth: function (t, b, c, d, a, p) {
3831         if (t == 0) {
3832             return b;
3833         }
3834
3835         if ((t /= d / 2) == 2) {
3836             return b + c;
3837         }
3838
3839         if (!p) {
3840             p = d * (.3 * 1.5);
3841         }
3842
3843         if (!a || a < Math.abs(c)) {
3844             a = c;
3845             var s = p / 4;
3846         }
3847         else {
3848             var s = p / (2 * Math.PI) * Math.asin(c / a);
3849         }
3850
3851         if (t < 1) {
3852             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3853                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3854         }
3855         return a * Math.pow(2, -10 * (t -= 1)) *
3856                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3857     },
3858
3859
3860
3861     backIn: function (t, b, c, d, s) {
3862         if (typeof s == 'undefined') {
3863             s = 1.70158;
3864         }
3865         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3866     },
3867
3868
3869     backOut: function (t, b, c, d, s) {
3870         if (typeof s == 'undefined') {
3871             s = 1.70158;
3872         }
3873         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3874     },
3875
3876
3877     backBoth: function (t, b, c, d, s) {
3878         if (typeof s == 'undefined') {
3879             s = 1.70158;
3880         }
3881
3882         if ((t /= d / 2 ) < 1) {
3883             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3884         }
3885         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3886     },
3887
3888
3889     bounceIn: function (t, b, c, d) {
3890         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3891     },
3892
3893
3894     bounceOut: function (t, b, c, d) {
3895         if ((t /= d) < (1 / 2.75)) {
3896             return c * (7.5625 * t * t) + b;
3897         } else if (t < (2 / 2.75)) {
3898             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3899         } else if (t < (2.5 / 2.75)) {
3900             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3901         }
3902         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3903     },
3904
3905
3906     bounceBoth: function (t, b, c, d) {
3907         if (t < d / 2) {
3908             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3909         }
3910         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3911     }
3912 };/*
3913  * Portions of this file are based on pieces of Yahoo User Interface Library
3914  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3915  * YUI licensed under the BSD License:
3916  * http://developer.yahoo.net/yui/license.txt
3917  * <script type="text/javascript">
3918  *
3919  */
3920     (function() {
3921         Roo.lib.Motion = function(el, attributes, duration, method) {
3922             if (el) {
3923                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3924             }
3925         };
3926
3927         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3928
3929
3930         var Y = Roo.lib;
3931         var superclass = Y.Motion.superclass;
3932         var proto = Y.Motion.prototype;
3933
3934         proto.toString = function() {
3935             var el = this.getEl();
3936             var id = el.id || el.tagName;
3937             return ("Motion " + id);
3938         };
3939
3940         proto.patterns.points = /^points$/i;
3941
3942         proto.setAttribute = function(attr, val, unit) {
3943             if (this.patterns.points.test(attr)) {
3944                 unit = unit || 'px';
3945                 superclass.setAttribute.call(this, 'left', val[0], unit);
3946                 superclass.setAttribute.call(this, 'top', val[1], unit);
3947             } else {
3948                 superclass.setAttribute.call(this, attr, val, unit);
3949             }
3950         };
3951
3952         proto.getAttribute = function(attr) {
3953             if (this.patterns.points.test(attr)) {
3954                 var val = [
3955                         superclass.getAttribute.call(this, 'left'),
3956                         superclass.getAttribute.call(this, 'top')
3957                         ];
3958             } else {
3959                 val = superclass.getAttribute.call(this, attr);
3960             }
3961
3962             return val;
3963         };
3964
3965         proto.doMethod = function(attr, start, end) {
3966             var val = null;
3967
3968             if (this.patterns.points.test(attr)) {
3969                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3970                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3971             } else {
3972                 val = superclass.doMethod.call(this, attr, start, end);
3973             }
3974             return val;
3975         };
3976
3977         proto.setRuntimeAttribute = function(attr) {
3978             if (this.patterns.points.test(attr)) {
3979                 var el = this.getEl();
3980                 var attributes = this.attributes;
3981                 var start;
3982                 var control = attributes['points']['control'] || [];
3983                 var end;
3984                 var i, len;
3985
3986                 if (control.length > 0 && !(control[0] instanceof Array)) {
3987                     control = [control];
3988                 } else {
3989                     var tmp = [];
3990                     for (i = 0,len = control.length; i < len; ++i) {
3991                         tmp[i] = control[i];
3992                     }
3993                     control = tmp;
3994                 }
3995
3996                 Roo.fly(el).position();
3997
3998                 if (isset(attributes['points']['from'])) {
3999                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4000                 }
4001                 else {
4002                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4003                 }
4004
4005                 start = this.getAttribute('points');
4006
4007
4008                 if (isset(attributes['points']['to'])) {
4009                     end = translateValues.call(this, attributes['points']['to'], start);
4010
4011                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4012                     for (i = 0,len = control.length; i < len; ++i) {
4013                         control[i] = translateValues.call(this, control[i], start);
4014                     }
4015
4016
4017                 } else if (isset(attributes['points']['by'])) {
4018                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4019
4020                     for (i = 0,len = control.length; i < len; ++i) {
4021                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4022                     }
4023                 }
4024
4025                 this.runtimeAttributes[attr] = [start];
4026
4027                 if (control.length > 0) {
4028                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4029                 }
4030
4031                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4032             }
4033             else {
4034                 superclass.setRuntimeAttribute.call(this, attr);
4035             }
4036         };
4037
4038         var translateValues = function(val, start) {
4039             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4040             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4041
4042             return val;
4043         };
4044
4045         var isset = function(prop) {
4046             return (typeof prop !== 'undefined');
4047         };
4048     })();
4049 /*
4050  * Portions of this file are based on pieces of Yahoo User Interface Library
4051  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4052  * YUI licensed under the BSD License:
4053  * http://developer.yahoo.net/yui/license.txt
4054  * <script type="text/javascript">
4055  *
4056  */
4057     (function() {
4058         Roo.lib.Scroll = function(el, attributes, duration, method) {
4059             if (el) {
4060                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4061             }
4062         };
4063
4064         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4065
4066
4067         var Y = Roo.lib;
4068         var superclass = Y.Scroll.superclass;
4069         var proto = Y.Scroll.prototype;
4070
4071         proto.toString = function() {
4072             var el = this.getEl();
4073             var id = el.id || el.tagName;
4074             return ("Scroll " + id);
4075         };
4076
4077         proto.doMethod = function(attr, start, end) {
4078             var val = null;
4079
4080             if (attr == 'scroll') {
4081                 val = [
4082                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4083                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4084                         ];
4085
4086             } else {
4087                 val = superclass.doMethod.call(this, attr, start, end);
4088             }
4089             return val;
4090         };
4091
4092         proto.getAttribute = function(attr) {
4093             var val = null;
4094             var el = this.getEl();
4095
4096             if (attr == 'scroll') {
4097                 val = [ el.scrollLeft, el.scrollTop ];
4098             } else {
4099                 val = superclass.getAttribute.call(this, attr);
4100             }
4101
4102             return val;
4103         };
4104
4105         proto.setAttribute = function(attr, val, unit) {
4106             var el = this.getEl();
4107
4108             if (attr == 'scroll') {
4109                 el.scrollLeft = val[0];
4110                 el.scrollTop = val[1];
4111             } else {
4112                 superclass.setAttribute.call(this, attr, val, unit);
4113             }
4114         };
4115     })();
4116 /*
4117  * Based on:
4118  * Ext JS Library 1.1.1
4119  * Copyright(c) 2006-2007, Ext JS, LLC.
4120  *
4121  * Originally Released Under LGPL - original licence link has changed is not relivant.
4122  *
4123  * Fork - LGPL
4124  * <script type="text/javascript">
4125  */
4126
4127
4128 // nasty IE9 hack - what a pile of crap that is..
4129
4130  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4131     Range.prototype.createContextualFragment = function (html) {
4132         var doc = window.document;
4133         var container = doc.createElement("div");
4134         container.innerHTML = html;
4135         var frag = doc.createDocumentFragment(), n;
4136         while ((n = container.firstChild)) {
4137             frag.appendChild(n);
4138         }
4139         return frag;
4140     };
4141 }
4142
4143 /**
4144  * @class Roo.DomHelper
4145  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4146  * 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>.
4147  * @singleton
4148  */
4149 Roo.DomHelper = function(){
4150     var tempTableEl = null;
4151     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4152     var tableRe = /^table|tbody|tr|td$/i;
4153     var xmlns = {};
4154     // build as innerHTML where available
4155     /** @ignore */
4156     var createHtml = function(o){
4157         if(typeof o == 'string'){
4158             return o;
4159         }
4160         var b = "";
4161         if(!o.tag){
4162             o.tag = "div";
4163         }
4164         b += "<" + o.tag;
4165         for(var attr in o){
4166             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4167             if(attr == "style"){
4168                 var s = o["style"];
4169                 if(typeof s == "function"){
4170                     s = s.call();
4171                 }
4172                 if(typeof s == "string"){
4173                     b += ' style="' + s + '"';
4174                 }else if(typeof s == "object"){
4175                     b += ' style="';
4176                     for(var key in s){
4177                         if(typeof s[key] != "function"){
4178                             b += key + ":" + s[key] + ";";
4179                         }
4180                     }
4181                     b += '"';
4182                 }
4183             }else{
4184                 if(attr == "cls"){
4185                     b += ' class="' + o["cls"] + '"';
4186                 }else if(attr == "htmlFor"){
4187                     b += ' for="' + o["htmlFor"] + '"';
4188                 }else{
4189                     b += " " + attr + '="' + o[attr] + '"';
4190                 }
4191             }
4192         }
4193         if(emptyTags.test(o.tag)){
4194             b += "/>";
4195         }else{
4196             b += ">";
4197             var cn = o.children || o.cn;
4198             if(cn){
4199                 //http://bugs.kde.org/show_bug.cgi?id=71506
4200                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4201                     for(var i = 0, len = cn.length; i < len; i++) {
4202                         b += createHtml(cn[i], b);
4203                     }
4204                 }else{
4205                     b += createHtml(cn, b);
4206                 }
4207             }
4208             if(o.html){
4209                 b += o.html;
4210             }
4211             b += "</" + o.tag + ">";
4212         }
4213         return b;
4214     };
4215
4216     // build as dom
4217     /** @ignore */
4218     var createDom = function(o, parentNode){
4219          
4220         // defininition craeted..
4221         var ns = false;
4222         if (o.ns && o.ns != 'html') {
4223                
4224             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4225                 xmlns[o.ns] = o.xmlns;
4226                 ns = o.xmlns;
4227             }
4228             if (typeof(xmlns[o.ns]) == 'undefined') {
4229                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4230             }
4231             ns = xmlns[o.ns];
4232         }
4233         
4234         
4235         if (typeof(o) == 'string') {
4236             return parentNode.appendChild(document.createTextNode(o));
4237         }
4238         o.tag = o.tag || div;
4239         if (o.ns && Roo.isIE) {
4240             ns = false;
4241             o.tag = o.ns + ':' + o.tag;
4242             
4243         }
4244         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4245         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4246         for(var attr in o){
4247             
4248             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4249                     attr == "style" || typeof o[attr] == "function") { continue; }
4250                     
4251             if(attr=="cls" && Roo.isIE){
4252                 el.className = o["cls"];
4253             }else{
4254                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4255                 else { 
4256                     el[attr] = o[attr];
4257                 }
4258             }
4259         }
4260         Roo.DomHelper.applyStyles(el, o.style);
4261         var cn = o.children || o.cn;
4262         if(cn){
4263             //http://bugs.kde.org/show_bug.cgi?id=71506
4264              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4265                 for(var i = 0, len = cn.length; i < len; i++) {
4266                     createDom(cn[i], el);
4267                 }
4268             }else{
4269                 createDom(cn, el);
4270             }
4271         }
4272         if(o.html){
4273             el.innerHTML = o.html;
4274         }
4275         if(parentNode){
4276            parentNode.appendChild(el);
4277         }
4278         return el;
4279     };
4280
4281     var ieTable = function(depth, s, h, e){
4282         tempTableEl.innerHTML = [s, h, e].join('');
4283         var i = -1, el = tempTableEl;
4284         while(++i < depth){
4285             el = el.firstChild;
4286         }
4287         return el;
4288     };
4289
4290     // kill repeat to save bytes
4291     var ts = '<table>',
4292         te = '</table>',
4293         tbs = ts+'<tbody>',
4294         tbe = '</tbody>'+te,
4295         trs = tbs + '<tr>',
4296         tre = '</tr>'+tbe;
4297
4298     /**
4299      * @ignore
4300      * Nasty code for IE's broken table implementation
4301      */
4302     var insertIntoTable = function(tag, where, el, html){
4303         if(!tempTableEl){
4304             tempTableEl = document.createElement('div');
4305         }
4306         var node;
4307         var before = null;
4308         if(tag == 'td'){
4309             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4310                 return;
4311             }
4312             if(where == 'beforebegin'){
4313                 before = el;
4314                 el = el.parentNode;
4315             } else{
4316                 before = el.nextSibling;
4317                 el = el.parentNode;
4318             }
4319             node = ieTable(4, trs, html, tre);
4320         }
4321         else if(tag == 'tr'){
4322             if(where == 'beforebegin'){
4323                 before = el;
4324                 el = el.parentNode;
4325                 node = ieTable(3, tbs, html, tbe);
4326             } else if(where == 'afterend'){
4327                 before = el.nextSibling;
4328                 el = el.parentNode;
4329                 node = ieTable(3, tbs, html, tbe);
4330             } else{ // INTO a TR
4331                 if(where == 'afterbegin'){
4332                     before = el.firstChild;
4333                 }
4334                 node = ieTable(4, trs, html, tre);
4335             }
4336         } else if(tag == 'tbody'){
4337             if(where == 'beforebegin'){
4338                 before = el;
4339                 el = el.parentNode;
4340                 node = ieTable(2, ts, html, te);
4341             } else if(where == 'afterend'){
4342                 before = el.nextSibling;
4343                 el = el.parentNode;
4344                 node = ieTable(2, ts, html, te);
4345             } else{
4346                 if(where == 'afterbegin'){
4347                     before = el.firstChild;
4348                 }
4349                 node = ieTable(3, tbs, html, tbe);
4350             }
4351         } else{ // TABLE
4352             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4353                 return;
4354             }
4355             if(where == 'afterbegin'){
4356                 before = el.firstChild;
4357             }
4358             node = ieTable(2, ts, html, te);
4359         }
4360         el.insertBefore(node, before);
4361         return node;
4362     };
4363
4364     return {
4365     /** True to force the use of DOM instead of html fragments @type Boolean */
4366     useDom : false,
4367
4368     /**
4369      * Returns the markup for the passed Element(s) config
4370      * @param {Object} o The Dom object spec (and children)
4371      * @return {String}
4372      */
4373     markup : function(o){
4374         return createHtml(o);
4375     },
4376
4377     /**
4378      * Applies a style specification to an element
4379      * @param {String/HTMLElement} el The element to apply styles to
4380      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4381      * a function which returns such a specification.
4382      */
4383     applyStyles : function(el, styles){
4384         if(styles){
4385            el = Roo.fly(el);
4386            if(typeof styles == "string"){
4387                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4388                var matches;
4389                while ((matches = re.exec(styles)) != null){
4390                    el.setStyle(matches[1], matches[2]);
4391                }
4392            }else if (typeof styles == "object"){
4393                for (var style in styles){
4394                   el.setStyle(style, styles[style]);
4395                }
4396            }else if (typeof styles == "function"){
4397                 Roo.DomHelper.applyStyles(el, styles.call());
4398            }
4399         }
4400     },
4401
4402     /**
4403      * Inserts an HTML fragment into the Dom
4404      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4405      * @param {HTMLElement} el The context element
4406      * @param {String} html The HTML fragmenet
4407      * @return {HTMLElement} The new node
4408      */
4409     insertHtml : function(where, el, html){
4410         where = where.toLowerCase();
4411         if(el.insertAdjacentHTML){
4412             if(tableRe.test(el.tagName)){
4413                 var rs;
4414                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4415                     return rs;
4416                 }
4417             }
4418             switch(where){
4419                 case "beforebegin":
4420                     el.insertAdjacentHTML('BeforeBegin', html);
4421                     return el.previousSibling;
4422                 case "afterbegin":
4423                     el.insertAdjacentHTML('AfterBegin', html);
4424                     return el.firstChild;
4425                 case "beforeend":
4426                     el.insertAdjacentHTML('BeforeEnd', html);
4427                     return el.lastChild;
4428                 case "afterend":
4429                     el.insertAdjacentHTML('AfterEnd', html);
4430                     return el.nextSibling;
4431             }
4432             throw 'Illegal insertion point -> "' + where + '"';
4433         }
4434         var range = el.ownerDocument.createRange();
4435         var frag;
4436         switch(where){
4437              case "beforebegin":
4438                 range.setStartBefore(el);
4439                 frag = range.createContextualFragment(html);
4440                 el.parentNode.insertBefore(frag, el);
4441                 return el.previousSibling;
4442              case "afterbegin":
4443                 if(el.firstChild){
4444                     range.setStartBefore(el.firstChild);
4445                     frag = range.createContextualFragment(html);
4446                     el.insertBefore(frag, el.firstChild);
4447                     return el.firstChild;
4448                 }else{
4449                     el.innerHTML = html;
4450                     return el.firstChild;
4451                 }
4452             case "beforeend":
4453                 if(el.lastChild){
4454                     range.setStartAfter(el.lastChild);
4455                     frag = range.createContextualFragment(html);
4456                     el.appendChild(frag);
4457                     return el.lastChild;
4458                 }else{
4459                     el.innerHTML = html;
4460                     return el.lastChild;
4461                 }
4462             case "afterend":
4463                 range.setStartAfter(el);
4464                 frag = range.createContextualFragment(html);
4465                 el.parentNode.insertBefore(frag, el.nextSibling);
4466                 return el.nextSibling;
4467             }
4468             throw 'Illegal insertion point -> "' + where + '"';
4469     },
4470
4471     /**
4472      * Creates new Dom element(s) and inserts them before el
4473      * @param {String/HTMLElement/Element} el The context element
4474      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4475      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4476      * @return {HTMLElement/Roo.Element} The new node
4477      */
4478     insertBefore : function(el, o, returnElement){
4479         return this.doInsert(el, o, returnElement, "beforeBegin");
4480     },
4481
4482     /**
4483      * Creates new Dom element(s) and inserts them after el
4484      * @param {String/HTMLElement/Element} el The context element
4485      * @param {Object} o The Dom object spec (and children)
4486      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4487      * @return {HTMLElement/Roo.Element} The new node
4488      */
4489     insertAfter : function(el, o, returnElement){
4490         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4491     },
4492
4493     /**
4494      * Creates new Dom element(s) and inserts them as the first child of el
4495      * @param {String/HTMLElement/Element} el The context element
4496      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4497      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4498      * @return {HTMLElement/Roo.Element} The new node
4499      */
4500     insertFirst : function(el, o, returnElement){
4501         return this.doInsert(el, o, returnElement, "afterBegin");
4502     },
4503
4504     // private
4505     doInsert : function(el, o, returnElement, pos, sibling){
4506         el = Roo.getDom(el);
4507         var newNode;
4508         if(this.useDom || o.ns){
4509             newNode = createDom(o, null);
4510             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4511         }else{
4512             var html = createHtml(o);
4513             newNode = this.insertHtml(pos, el, html);
4514         }
4515         return returnElement ? Roo.get(newNode, true) : newNode;
4516     },
4517
4518     /**
4519      * Creates new Dom element(s) and appends them to el
4520      * @param {String/HTMLElement/Element} el The context element
4521      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4522      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4523      * @return {HTMLElement/Roo.Element} The new node
4524      */
4525     append : function(el, o, returnElement){
4526         el = Roo.getDom(el);
4527         var newNode;
4528         if(this.useDom || o.ns){
4529             newNode = createDom(o, null);
4530             el.appendChild(newNode);
4531         }else{
4532             var html = createHtml(o);
4533             newNode = this.insertHtml("beforeEnd", el, html);
4534         }
4535         return returnElement ? Roo.get(newNode, true) : newNode;
4536     },
4537
4538     /**
4539      * Creates new Dom element(s) and overwrites the contents of el with them
4540      * @param {String/HTMLElement/Element} el The context element
4541      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4542      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4543      * @return {HTMLElement/Roo.Element} The new node
4544      */
4545     overwrite : function(el, o, returnElement){
4546         el = Roo.getDom(el);
4547         if (o.ns) {
4548           
4549             while (el.childNodes.length) {
4550                 el.removeChild(el.firstChild);
4551             }
4552             createDom(o, el);
4553         } else {
4554             el.innerHTML = createHtml(o);   
4555         }
4556         
4557         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4558     },
4559
4560     /**
4561      * Creates a new Roo.DomHelper.Template from the Dom object spec
4562      * @param {Object} o The Dom object spec (and children)
4563      * @return {Roo.DomHelper.Template} The new template
4564      */
4565     createTemplate : function(o){
4566         var html = createHtml(o);
4567         return new Roo.Template(html);
4568     }
4569     };
4570 }();
4571 /*
4572  * Based on:
4573  * Ext JS Library 1.1.1
4574  * Copyright(c) 2006-2007, Ext JS, LLC.
4575  *
4576  * Originally Released Under LGPL - original licence link has changed is not relivant.
4577  *
4578  * Fork - LGPL
4579  * <script type="text/javascript">
4580  */
4581  
4582 /**
4583 * @class Roo.Template
4584 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4585 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4586 * Usage:
4587 <pre><code>
4588 var t = new Roo.Template({
4589     html :  '&lt;div name="{id}"&gt;' + 
4590         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4591         '&lt;/div&gt;',
4592     myformat: function (value, allValues) {
4593         return 'XX' + value;
4594     }
4595 });
4596 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4597 </code></pre>
4598 * For more information see this blog post with examples:
4599 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4600      - Create Elements using DOM, HTML fragments and Templates</a>. 
4601 * @constructor
4602 * @param {Object} cfg - Configuration object.
4603 */
4604 Roo.Template = function(cfg){
4605     // BC!
4606     if(cfg instanceof Array){
4607         cfg = cfg.join("");
4608     }else if(arguments.length > 1){
4609         cfg = Array.prototype.join.call(arguments, "");
4610     }
4611     
4612     
4613     if (typeof(cfg) == 'object') {
4614         Roo.apply(this,cfg)
4615     } else {
4616         // bc
4617         this.html = cfg;
4618     }
4619     if (this.url) {
4620         this.load();
4621     }
4622     
4623 };
4624 Roo.Template.prototype = {
4625     
4626     /**
4627      * @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..
4628      *                    it should be fixed so that template is observable...
4629      */
4630     url : false,
4631     /**
4632      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4633      */
4634     html : '',
4635     /**
4636      * Returns an HTML fragment of this template with the specified values applied.
4637      * @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'})
4638      * @return {String} The HTML fragment
4639      */
4640     applyTemplate : function(values){
4641         try {
4642            
4643             if(this.compiled){
4644                 return this.compiled(values);
4645             }
4646             var useF = this.disableFormats !== true;
4647             var fm = Roo.util.Format, tpl = this;
4648             var fn = function(m, name, format, args){
4649                 if(format && useF){
4650                     if(format.substr(0, 5) == "this."){
4651                         return tpl.call(format.substr(5), values[name], values);
4652                     }else{
4653                         if(args){
4654                             // quoted values are required for strings in compiled templates, 
4655                             // but for non compiled we need to strip them
4656                             // quoted reversed for jsmin
4657                             var re = /^\s*['"](.*)["']\s*$/;
4658                             args = args.split(',');
4659                             for(var i = 0, len = args.length; i < len; i++){
4660                                 args[i] = args[i].replace(re, "$1");
4661                             }
4662                             args = [values[name]].concat(args);
4663                         }else{
4664                             args = [values[name]];
4665                         }
4666                         return fm[format].apply(fm, args);
4667                     }
4668                 }else{
4669                     return values[name] !== undefined ? values[name] : "";
4670                 }
4671             };
4672             return this.html.replace(this.re, fn);
4673         } catch (e) {
4674             Roo.log(e);
4675             throw e;
4676         }
4677          
4678     },
4679     
4680     loading : false,
4681       
4682     load : function ()
4683     {
4684          
4685         if (this.loading) {
4686             return;
4687         }
4688         var _t = this;
4689         
4690         this.loading = true;
4691         this.compiled = false;
4692         
4693         var cx = new Roo.data.Connection();
4694         cx.request({
4695             url : this.url,
4696             method : 'GET',
4697             success : function (response) {
4698                 _t.loading = false;
4699                 _t.html = response.responseText;
4700                 _t.url = false;
4701                 _t.compile();
4702              },
4703             failure : function(response) {
4704                 Roo.log("Template failed to load from " + _t.url);
4705                 _t.loading = false;
4706             }
4707         });
4708     },
4709
4710     /**
4711      * Sets the HTML used as the template and optionally compiles it.
4712      * @param {String} html
4713      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4714      * @return {Roo.Template} this
4715      */
4716     set : function(html, compile){
4717         this.html = html;
4718         this.compiled = null;
4719         if(compile){
4720             this.compile();
4721         }
4722         return this;
4723     },
4724     
4725     /**
4726      * True to disable format functions (defaults to false)
4727      * @type Boolean
4728      */
4729     disableFormats : false,
4730     
4731     /**
4732     * The regular expression used to match template variables 
4733     * @type RegExp
4734     * @property 
4735     */
4736     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4737     
4738     /**
4739      * Compiles the template into an internal function, eliminating the RegEx overhead.
4740      * @return {Roo.Template} this
4741      */
4742     compile : function(){
4743         var fm = Roo.util.Format;
4744         var useF = this.disableFormats !== true;
4745         var sep = Roo.isGecko ? "+" : ",";
4746         var fn = function(m, name, format, args){
4747             if(format && useF){
4748                 args = args ? ',' + args : "";
4749                 if(format.substr(0, 5) != "this."){
4750                     format = "fm." + format + '(';
4751                 }else{
4752                     format = 'this.call("'+ format.substr(5) + '", ';
4753                     args = ", values";
4754                 }
4755             }else{
4756                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4757             }
4758             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4759         };
4760         var body;
4761         // branched to use + in gecko and [].join() in others
4762         if(Roo.isGecko){
4763             body = "this.compiled = function(values){ return '" +
4764                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4765                     "';};";
4766         }else{
4767             body = ["this.compiled = function(values){ return ['"];
4768             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4769             body.push("'].join('');};");
4770             body = body.join('');
4771         }
4772         /**
4773          * eval:var:values
4774          * eval:var:fm
4775          */
4776         eval(body);
4777         return this;
4778     },
4779     
4780     // private function used to call members
4781     call : function(fnName, value, allValues){
4782         return this[fnName](value, allValues);
4783     },
4784     
4785     /**
4786      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4787      * @param {String/HTMLElement/Roo.Element} el The context element
4788      * @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'})
4789      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4790      * @return {HTMLElement/Roo.Element} The new node or Element
4791      */
4792     insertFirst: function(el, values, returnElement){
4793         return this.doInsert('afterBegin', el, values, returnElement);
4794     },
4795
4796     /**
4797      * Applies the supplied values to the template and inserts the new node(s) before el.
4798      * @param {String/HTMLElement/Roo.Element} el The context element
4799      * @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'})
4800      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4801      * @return {HTMLElement/Roo.Element} The new node or Element
4802      */
4803     insertBefore: function(el, values, returnElement){
4804         return this.doInsert('beforeBegin', el, values, returnElement);
4805     },
4806
4807     /**
4808      * Applies the supplied values to the template and inserts the new node(s) after el.
4809      * @param {String/HTMLElement/Roo.Element} el The context element
4810      * @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'})
4811      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4812      * @return {HTMLElement/Roo.Element} The new node or Element
4813      */
4814     insertAfter : function(el, values, returnElement){
4815         return this.doInsert('afterEnd', el, values, returnElement);
4816     },
4817     
4818     /**
4819      * Applies the supplied values to the template and appends the new node(s) to 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     append : function(el, values, returnElement){
4826         return this.doInsert('beforeEnd', el, values, returnElement);
4827     },
4828
4829     doInsert : function(where, el, values, returnEl){
4830         el = Roo.getDom(el);
4831         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4832         return returnEl ? Roo.get(newNode, true) : newNode;
4833     },
4834
4835     /**
4836      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4837      * @param {String/HTMLElement/Roo.Element} el The context element
4838      * @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'})
4839      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4840      * @return {HTMLElement/Roo.Element} The new node or Element
4841      */
4842     overwrite : function(el, values, returnElement){
4843         el = Roo.getDom(el);
4844         el.innerHTML = this.applyTemplate(values);
4845         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4846     }
4847 };
4848 /**
4849  * Alias for {@link #applyTemplate}
4850  * @method
4851  */
4852 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4853
4854 // backwards compat
4855 Roo.DomHelper.Template = Roo.Template;
4856
4857 /**
4858  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4859  * @param {String/HTMLElement} el A DOM element or its id
4860  * @returns {Roo.Template} The created template
4861  * @static
4862  */
4863 Roo.Template.from = function(el){
4864     el = Roo.getDom(el);
4865     return new Roo.Template(el.value || el.innerHTML);
4866 };/*
4867  * Based on:
4868  * Ext JS Library 1.1.1
4869  * Copyright(c) 2006-2007, Ext JS, LLC.
4870  *
4871  * Originally Released Under LGPL - original licence link has changed is not relivant.
4872  *
4873  * Fork - LGPL
4874  * <script type="text/javascript">
4875  */
4876  
4877
4878 /*
4879  * This is code is also distributed under MIT license for use
4880  * with jQuery and prototype JavaScript libraries.
4881  */
4882 /**
4883  * @class Roo.DomQuery
4884 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).
4885 <p>
4886 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>
4887
4888 <p>
4889 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.
4890 </p>
4891 <h4>Element Selectors:</h4>
4892 <ul class="list">
4893     <li> <b>*</b> any element</li>
4894     <li> <b>E</b> an element with the tag E</li>
4895     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4896     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4897     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4898     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4899 </ul>
4900 <h4>Attribute Selectors:</h4>
4901 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4902 <ul class="list">
4903     <li> <b>E[foo]</b> has an attribute "foo"</li>
4904     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4905     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4906     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4907     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4908     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4909     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4910 </ul>
4911 <h4>Pseudo Classes:</h4>
4912 <ul class="list">
4913     <li> <b>E:first-child</b> E is the first child of its parent</li>
4914     <li> <b>E:last-child</b> E is the last child of its parent</li>
4915     <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>
4916     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4917     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4918     <li> <b>E:only-child</b> E is the only child of its parent</li>
4919     <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>
4920     <li> <b>E:first</b> the first E in the resultset</li>
4921     <li> <b>E:last</b> the last E in the resultset</li>
4922     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4923     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4924     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4925     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4926     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4927     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4928     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4929     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4930     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4931 </ul>
4932 <h4>CSS Value Selectors:</h4>
4933 <ul class="list">
4934     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4935     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4936     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4937     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4938     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4939     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4940 </ul>
4941  * @singleton
4942  */
4943 Roo.DomQuery = function(){
4944     var cache = {}, simpleCache = {}, valueCache = {};
4945     var nonSpace = /\S/;
4946     var trimRe = /^\s+|\s+$/g;
4947     var tplRe = /\{(\d+)\}/g;
4948     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4949     var tagTokenRe = /^(#)?([\w-\*]+)/;
4950     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4951
4952     function child(p, index){
4953         var i = 0;
4954         var n = p.firstChild;
4955         while(n){
4956             if(n.nodeType == 1){
4957                if(++i == index){
4958                    return n;
4959                }
4960             }
4961             n = n.nextSibling;
4962         }
4963         return null;
4964     };
4965
4966     function next(n){
4967         while((n = n.nextSibling) && n.nodeType != 1);
4968         return n;
4969     };
4970
4971     function prev(n){
4972         while((n = n.previousSibling) && n.nodeType != 1);
4973         return n;
4974     };
4975
4976     function children(d){
4977         var n = d.firstChild, ni = -1;
4978             while(n){
4979                 var nx = n.nextSibling;
4980                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4981                     d.removeChild(n);
4982                 }else{
4983                     n.nodeIndex = ++ni;
4984                 }
4985                 n = nx;
4986             }
4987             return this;
4988         };
4989
4990     function byClassName(c, a, v){
4991         if(!v){
4992             return c;
4993         }
4994         var r = [], ri = -1, cn;
4995         for(var i = 0, ci; ci = c[i]; i++){
4996             if((' '+ci.className+' ').indexOf(v) != -1){
4997                 r[++ri] = ci;
4998             }
4999         }
5000         return r;
5001     };
5002
5003     function attrValue(n, attr){
5004         if(!n.tagName && typeof n.length != "undefined"){
5005             n = n[0];
5006         }
5007         if(!n){
5008             return null;
5009         }
5010         if(attr == "for"){
5011             return n.htmlFor;
5012         }
5013         if(attr == "class" || attr == "className"){
5014             return n.className;
5015         }
5016         return n.getAttribute(attr) || n[attr];
5017
5018     };
5019
5020     function getNodes(ns, mode, tagName){
5021         var result = [], ri = -1, cs;
5022         if(!ns){
5023             return result;
5024         }
5025         tagName = tagName || "*";
5026         if(typeof ns.getElementsByTagName != "undefined"){
5027             ns = [ns];
5028         }
5029         if(!mode){
5030             for(var i = 0, ni; ni = ns[i]; i++){
5031                 cs = ni.getElementsByTagName(tagName);
5032                 for(var j = 0, ci; ci = cs[j]; j++){
5033                     result[++ri] = ci;
5034                 }
5035             }
5036         }else if(mode == "/" || mode == ">"){
5037             var utag = tagName.toUpperCase();
5038             for(var i = 0, ni, cn; ni = ns[i]; i++){
5039                 cn = ni.children || ni.childNodes;
5040                 for(var j = 0, cj; cj = cn[j]; j++){
5041                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5042                         result[++ri] = cj;
5043                     }
5044                 }
5045             }
5046         }else if(mode == "+"){
5047             var utag = tagName.toUpperCase();
5048             for(var i = 0, n; n = ns[i]; i++){
5049                 while((n = n.nextSibling) && n.nodeType != 1);
5050                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5051                     result[++ri] = n;
5052                 }
5053             }
5054         }else if(mode == "~"){
5055             for(var i = 0, n; n = ns[i]; i++){
5056                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5057                 if(n){
5058                     result[++ri] = n;
5059                 }
5060             }
5061         }
5062         return result;
5063     };
5064
5065     function concat(a, b){
5066         if(b.slice){
5067             return a.concat(b);
5068         }
5069         for(var i = 0, l = b.length; i < l; i++){
5070             a[a.length] = b[i];
5071         }
5072         return a;
5073     }
5074
5075     function byTag(cs, tagName){
5076         if(cs.tagName || cs == document){
5077             cs = [cs];
5078         }
5079         if(!tagName){
5080             return cs;
5081         }
5082         var r = [], ri = -1;
5083         tagName = tagName.toLowerCase();
5084         for(var i = 0, ci; ci = cs[i]; i++){
5085             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5086                 r[++ri] = ci;
5087             }
5088         }
5089         return r;
5090     };
5091
5092     function byId(cs, attr, id){
5093         if(cs.tagName || cs == document){
5094             cs = [cs];
5095         }
5096         if(!id){
5097             return cs;
5098         }
5099         var r = [], ri = -1;
5100         for(var i = 0,ci; ci = cs[i]; i++){
5101             if(ci && ci.id == id){
5102                 r[++ri] = ci;
5103                 return r;
5104             }
5105         }
5106         return r;
5107     };
5108
5109     function byAttribute(cs, attr, value, op, custom){
5110         var r = [], ri = -1, st = custom=="{";
5111         var f = Roo.DomQuery.operators[op];
5112         for(var i = 0, ci; ci = cs[i]; i++){
5113             var a;
5114             if(st){
5115                 a = Roo.DomQuery.getStyle(ci, attr);
5116             }
5117             else if(attr == "class" || attr == "className"){
5118                 a = ci.className;
5119             }else if(attr == "for"){
5120                 a = ci.htmlFor;
5121             }else if(attr == "href"){
5122                 a = ci.getAttribute("href", 2);
5123             }else{
5124                 a = ci.getAttribute(attr);
5125             }
5126             if((f && f(a, value)) || (!f && a)){
5127                 r[++ri] = ci;
5128             }
5129         }
5130         return r;
5131     };
5132
5133     function byPseudo(cs, name, value){
5134         return Roo.DomQuery.pseudos[name](cs, value);
5135     };
5136
5137     // This is for IE MSXML which does not support expandos.
5138     // IE runs the same speed using setAttribute, however FF slows way down
5139     // and Safari completely fails so they need to continue to use expandos.
5140     var isIE = window.ActiveXObject ? true : false;
5141
5142     // this eval is stop the compressor from
5143     // renaming the variable to something shorter
5144     
5145     /** eval:var:batch */
5146     var batch = 30803; 
5147
5148     var key = 30803;
5149
5150     function nodupIEXml(cs){
5151         var d = ++key;
5152         cs[0].setAttribute("_nodup", d);
5153         var r = [cs[0]];
5154         for(var i = 1, len = cs.length; i < len; i++){
5155             var c = cs[i];
5156             if(!c.getAttribute("_nodup") != d){
5157                 c.setAttribute("_nodup", d);
5158                 r[r.length] = c;
5159             }
5160         }
5161         for(var i = 0, len = cs.length; i < len; i++){
5162             cs[i].removeAttribute("_nodup");
5163         }
5164         return r;
5165     }
5166
5167     function nodup(cs){
5168         if(!cs){
5169             return [];
5170         }
5171         var len = cs.length, c, i, r = cs, cj, ri = -1;
5172         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5173             return cs;
5174         }
5175         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5176             return nodupIEXml(cs);
5177         }
5178         var d = ++key;
5179         cs[0]._nodup = d;
5180         for(i = 1; c = cs[i]; i++){
5181             if(c._nodup != d){
5182                 c._nodup = d;
5183             }else{
5184                 r = [];
5185                 for(var j = 0; j < i; j++){
5186                     r[++ri] = cs[j];
5187                 }
5188                 for(j = i+1; cj = cs[j]; j++){
5189                     if(cj._nodup != d){
5190                         cj._nodup = d;
5191                         r[++ri] = cj;
5192                     }
5193                 }
5194                 return r;
5195             }
5196         }
5197         return r;
5198     }
5199
5200     function quickDiffIEXml(c1, c2){
5201         var d = ++key;
5202         for(var i = 0, len = c1.length; i < len; i++){
5203             c1[i].setAttribute("_qdiff", d);
5204         }
5205         var r = [];
5206         for(var i = 0, len = c2.length; i < len; i++){
5207             if(c2[i].getAttribute("_qdiff") != d){
5208                 r[r.length] = c2[i];
5209             }
5210         }
5211         for(var i = 0, len = c1.length; i < len; i++){
5212            c1[i].removeAttribute("_qdiff");
5213         }
5214         return r;
5215     }
5216
5217     function quickDiff(c1, c2){
5218         var len1 = c1.length;
5219         if(!len1){
5220             return c2;
5221         }
5222         if(isIE && c1[0].selectSingleNode){
5223             return quickDiffIEXml(c1, c2);
5224         }
5225         var d = ++key;
5226         for(var i = 0; i < len1; i++){
5227             c1[i]._qdiff = d;
5228         }
5229         var r = [];
5230         for(var i = 0, len = c2.length; i < len; i++){
5231             if(c2[i]._qdiff != d){
5232                 r[r.length] = c2[i];
5233             }
5234         }
5235         return r;
5236     }
5237
5238     function quickId(ns, mode, root, id){
5239         if(ns == root){
5240            var d = root.ownerDocument || root;
5241            return d.getElementById(id);
5242         }
5243         ns = getNodes(ns, mode, "*");
5244         return byId(ns, null, id);
5245     }
5246
5247     return {
5248         getStyle : function(el, name){
5249             return Roo.fly(el).getStyle(name);
5250         },
5251         /**
5252          * Compiles a selector/xpath query into a reusable function. The returned function
5253          * takes one parameter "root" (optional), which is the context node from where the query should start.
5254          * @param {String} selector The selector/xpath query
5255          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5256          * @return {Function}
5257          */
5258         compile : function(path, type){
5259             type = type || "select";
5260             
5261             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5262             var q = path, mode, lq;
5263             var tk = Roo.DomQuery.matchers;
5264             var tklen = tk.length;
5265             var mm;
5266
5267             // accept leading mode switch
5268             var lmode = q.match(modeRe);
5269             if(lmode && lmode[1]){
5270                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5271                 q = q.replace(lmode[1], "");
5272             }
5273             // strip leading slashes
5274             while(path.substr(0, 1)=="/"){
5275                 path = path.substr(1);
5276             }
5277
5278             while(q && lq != q){
5279                 lq = q;
5280                 var tm = q.match(tagTokenRe);
5281                 if(type == "select"){
5282                     if(tm){
5283                         if(tm[1] == "#"){
5284                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5285                         }else{
5286                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5287                         }
5288                         q = q.replace(tm[0], "");
5289                     }else if(q.substr(0, 1) != '@'){
5290                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5291                     }
5292                 }else{
5293                     if(tm){
5294                         if(tm[1] == "#"){
5295                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5296                         }else{
5297                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5298                         }
5299                         q = q.replace(tm[0], "");
5300                     }
5301                 }
5302                 while(!(mm = q.match(modeRe))){
5303                     var matched = false;
5304                     for(var j = 0; j < tklen; j++){
5305                         var t = tk[j];
5306                         var m = q.match(t.re);
5307                         if(m){
5308                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5309                                                     return m[i];
5310                                                 });
5311                             q = q.replace(m[0], "");
5312                             matched = true;
5313                             break;
5314                         }
5315                     }
5316                     // prevent infinite loop on bad selector
5317                     if(!matched){
5318                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5319                     }
5320                 }
5321                 if(mm[1]){
5322                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5323                     q = q.replace(mm[1], "");
5324                 }
5325             }
5326             fn[fn.length] = "return nodup(n);\n}";
5327             
5328              /** 
5329               * list of variables that need from compression as they are used by eval.
5330              *  eval:var:batch 
5331              *  eval:var:nodup
5332              *  eval:var:byTag
5333              *  eval:var:ById
5334              *  eval:var:getNodes
5335              *  eval:var:quickId
5336              *  eval:var:mode
5337              *  eval:var:root
5338              *  eval:var:n
5339              *  eval:var:byClassName
5340              *  eval:var:byPseudo
5341              *  eval:var:byAttribute
5342              *  eval:var:attrValue
5343              * 
5344              **/ 
5345             eval(fn.join(""));
5346             return f;
5347         },
5348
5349         /**
5350          * Selects a group of elements.
5351          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5352          * @param {Node} root (optional) The start of the query (defaults to document).
5353          * @return {Array}
5354          */
5355         select : function(path, root, type){
5356             if(!root || root == document){
5357                 root = document;
5358             }
5359             if(typeof root == "string"){
5360                 root = document.getElementById(root);
5361             }
5362             var paths = path.split(",");
5363             var results = [];
5364             for(var i = 0, len = paths.length; i < len; i++){
5365                 var p = paths[i].replace(trimRe, "");
5366                 if(!cache[p]){
5367                     cache[p] = Roo.DomQuery.compile(p);
5368                     if(!cache[p]){
5369                         throw p + " is not a valid selector";
5370                     }
5371                 }
5372                 var result = cache[p](root);
5373                 if(result && result != document){
5374                     results = results.concat(result);
5375                 }
5376             }
5377             if(paths.length > 1){
5378                 return nodup(results);
5379             }
5380             return results;
5381         },
5382
5383         /**
5384          * Selects a single element.
5385          * @param {String} selector The selector/xpath query
5386          * @param {Node} root (optional) The start of the query (defaults to document).
5387          * @return {Element}
5388          */
5389         selectNode : function(path, root){
5390             return Roo.DomQuery.select(path, root)[0];
5391         },
5392
5393         /**
5394          * Selects the value of a node, optionally replacing null with the defaultValue.
5395          * @param {String} selector The selector/xpath query
5396          * @param {Node} root (optional) The start of the query (defaults to document).
5397          * @param {String} defaultValue
5398          */
5399         selectValue : function(path, root, defaultValue){
5400             path = path.replace(trimRe, "");
5401             if(!valueCache[path]){
5402                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5403             }
5404             var n = valueCache[path](root);
5405             n = n[0] ? n[0] : n;
5406             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5407             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5408         },
5409
5410         /**
5411          * Selects the value of a node, parsing integers and floats.
5412          * @param {String} selector The selector/xpath query
5413          * @param {Node} root (optional) The start of the query (defaults to document).
5414          * @param {Number} defaultValue
5415          * @return {Number}
5416          */
5417         selectNumber : function(path, root, defaultValue){
5418             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5419             return parseFloat(v);
5420         },
5421
5422         /**
5423          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5424          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5425          * @param {String} selector The simple selector to test
5426          * @return {Boolean}
5427          */
5428         is : function(el, ss){
5429             if(typeof el == "string"){
5430                 el = document.getElementById(el);
5431             }
5432             var isArray = (el instanceof Array);
5433             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5434             return isArray ? (result.length == el.length) : (result.length > 0);
5435         },
5436
5437         /**
5438          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5439          * @param {Array} el An array of elements to filter
5440          * @param {String} selector The simple selector to test
5441          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5442          * the selector instead of the ones that match
5443          * @return {Array}
5444          */
5445         filter : function(els, ss, nonMatches){
5446             ss = ss.replace(trimRe, "");
5447             if(!simpleCache[ss]){
5448                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5449             }
5450             var result = simpleCache[ss](els);
5451             return nonMatches ? quickDiff(result, els) : result;
5452         },
5453
5454         /**
5455          * Collection of matching regular expressions and code snippets.
5456          */
5457         matchers : [{
5458                 re: /^\.([\w-]+)/,
5459                 select: 'n = byClassName(n, null, " {1} ");'
5460             }, {
5461                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5462                 select: 'n = byPseudo(n, "{1}", "{2}");'
5463             },{
5464                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5465                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5466             }, {
5467                 re: /^#([\w-]+)/,
5468                 select: 'n = byId(n, null, "{1}");'
5469             },{
5470                 re: /^@([\w-]+)/,
5471                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5472             }
5473         ],
5474
5475         /**
5476          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5477          * 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;.
5478          */
5479         operators : {
5480             "=" : function(a, v){
5481                 return a == v;
5482             },
5483             "!=" : function(a, v){
5484                 return a != v;
5485             },
5486             "^=" : function(a, v){
5487                 return a && a.substr(0, v.length) == v;
5488             },
5489             "$=" : function(a, v){
5490                 return a && a.substr(a.length-v.length) == v;
5491             },
5492             "*=" : function(a, v){
5493                 return a && a.indexOf(v) !== -1;
5494             },
5495             "%=" : function(a, v){
5496                 return (a % v) == 0;
5497             },
5498             "|=" : function(a, v){
5499                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5500             },
5501             "~=" : function(a, v){
5502                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5503             }
5504         },
5505
5506         /**
5507          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5508          * and the argument (if any) supplied in the selector.
5509          */
5510         pseudos : {
5511             "first-child" : function(c){
5512                 var r = [], ri = -1, n;
5513                 for(var i = 0, ci; ci = n = c[i]; i++){
5514                     while((n = n.previousSibling) && n.nodeType != 1);
5515                     if(!n){
5516                         r[++ri] = ci;
5517                     }
5518                 }
5519                 return r;
5520             },
5521
5522             "last-child" : function(c){
5523                 var r = [], ri = -1, n;
5524                 for(var i = 0, ci; ci = n = c[i]; i++){
5525                     while((n = n.nextSibling) && n.nodeType != 1);
5526                     if(!n){
5527                         r[++ri] = ci;
5528                     }
5529                 }
5530                 return r;
5531             },
5532
5533             "nth-child" : function(c, a) {
5534                 var r = [], ri = -1;
5535                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5536                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5537                 for(var i = 0, n; n = c[i]; i++){
5538                     var pn = n.parentNode;
5539                     if (batch != pn._batch) {
5540                         var j = 0;
5541                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5542                             if(cn.nodeType == 1){
5543                                cn.nodeIndex = ++j;
5544                             }
5545                         }
5546                         pn._batch = batch;
5547                     }
5548                     if (f == 1) {
5549                         if (l == 0 || n.nodeIndex == l){
5550                             r[++ri] = n;
5551                         }
5552                     } else if ((n.nodeIndex + l) % f == 0){
5553                         r[++ri] = n;
5554                     }
5555                 }
5556
5557                 return r;
5558             },
5559
5560             "only-child" : function(c){
5561                 var r = [], ri = -1;;
5562                 for(var i = 0, ci; ci = c[i]; i++){
5563                     if(!prev(ci) && !next(ci)){
5564                         r[++ri] = ci;
5565                     }
5566                 }
5567                 return r;
5568             },
5569
5570             "empty" : function(c){
5571                 var r = [], ri = -1;
5572                 for(var i = 0, ci; ci = c[i]; i++){
5573                     var cns = ci.childNodes, j = 0, cn, empty = true;
5574                     while(cn = cns[j]){
5575                         ++j;
5576                         if(cn.nodeType == 1 || cn.nodeType == 3){
5577                             empty = false;
5578                             break;
5579                         }
5580                     }
5581                     if(empty){
5582                         r[++ri] = ci;
5583                     }
5584                 }
5585                 return r;
5586             },
5587
5588             "contains" : function(c, v){
5589                 var r = [], ri = -1;
5590                 for(var i = 0, ci; ci = c[i]; i++){
5591                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5592                         r[++ri] = ci;
5593                     }
5594                 }
5595                 return r;
5596             },
5597
5598             "nodeValue" : function(c, v){
5599                 var r = [], ri = -1;
5600                 for(var i = 0, ci; ci = c[i]; i++){
5601                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5602                         r[++ri] = ci;
5603                     }
5604                 }
5605                 return r;
5606             },
5607
5608             "checked" : function(c){
5609                 var r = [], ri = -1;
5610                 for(var i = 0, ci; ci = c[i]; i++){
5611                     if(ci.checked == true){
5612                         r[++ri] = ci;
5613                     }
5614                 }
5615                 return r;
5616             },
5617
5618             "not" : function(c, ss){
5619                 return Roo.DomQuery.filter(c, ss, true);
5620             },
5621
5622             "odd" : function(c){
5623                 return this["nth-child"](c, "odd");
5624             },
5625
5626             "even" : function(c){
5627                 return this["nth-child"](c, "even");
5628             },
5629
5630             "nth" : function(c, a){
5631                 return c[a-1] || [];
5632             },
5633
5634             "first" : function(c){
5635                 return c[0] || [];
5636             },
5637
5638             "last" : function(c){
5639                 return c[c.length-1] || [];
5640             },
5641
5642             "has" : function(c, ss){
5643                 var s = Roo.DomQuery.select;
5644                 var r = [], ri = -1;
5645                 for(var i = 0, ci; ci = c[i]; i++){
5646                     if(s(ss, ci).length > 0){
5647                         r[++ri] = ci;
5648                     }
5649                 }
5650                 return r;
5651             },
5652
5653             "next" : function(c, ss){
5654                 var is = Roo.DomQuery.is;
5655                 var r = [], ri = -1;
5656                 for(var i = 0, ci; ci = c[i]; i++){
5657                     var n = next(ci);
5658                     if(n && is(n, ss)){
5659                         r[++ri] = ci;
5660                     }
5661                 }
5662                 return r;
5663             },
5664
5665             "prev" : function(c, ss){
5666                 var is = Roo.DomQuery.is;
5667                 var r = [], ri = -1;
5668                 for(var i = 0, ci; ci = c[i]; i++){
5669                     var n = prev(ci);
5670                     if(n && is(n, ss)){
5671                         r[++ri] = ci;
5672                     }
5673                 }
5674                 return r;
5675             }
5676         }
5677     };
5678 }();
5679
5680 /**
5681  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5682  * @param {String} path The selector/xpath query
5683  * @param {Node} root (optional) The start of the query (defaults to document).
5684  * @return {Array}
5685  * @member Roo
5686  * @method query
5687  */
5688 Roo.query = Roo.DomQuery.select;
5689 /*
5690  * Based on:
5691  * Ext JS Library 1.1.1
5692  * Copyright(c) 2006-2007, Ext JS, LLC.
5693  *
5694  * Originally Released Under LGPL - original licence link has changed is not relivant.
5695  *
5696  * Fork - LGPL
5697  * <script type="text/javascript">
5698  */
5699
5700 /**
5701  * @class Roo.util.Observable
5702  * Base class that provides a common interface for publishing events. Subclasses are expected to
5703  * to have a property "events" with all the events defined.<br>
5704  * For example:
5705  * <pre><code>
5706  Employee = function(name){
5707     this.name = name;
5708     this.addEvents({
5709         "fired" : true,
5710         "quit" : true
5711     });
5712  }
5713  Roo.extend(Employee, Roo.util.Observable);
5714 </code></pre>
5715  * @param {Object} config properties to use (incuding events / listeners)
5716  */
5717
5718 Roo.util.Observable = function(cfg){
5719     
5720     cfg = cfg|| {};
5721     this.addEvents(cfg.events || {});
5722     if (cfg.events) {
5723         delete cfg.events; // make sure
5724     }
5725      
5726     Roo.apply(this, cfg);
5727     
5728     if(this.listeners){
5729         this.on(this.listeners);
5730         delete this.listeners;
5731     }
5732 };
5733 Roo.util.Observable.prototype = {
5734     /** 
5735  * @cfg {Object} listeners  list of events and functions to call for this object, 
5736  * For example :
5737  * <pre><code>
5738     listeners :  { 
5739        'click' : function(e) {
5740            ..... 
5741         } ,
5742         .... 
5743     } 
5744   </code></pre>
5745  */
5746     
5747     
5748     /**
5749      * Fires the specified event with the passed parameters (minus the event name).
5750      * @param {String} eventName
5751      * @param {Object...} args Variable number of parameters are passed to handlers
5752      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5753      */
5754     fireEvent : function(){
5755         var ce = this.events[arguments[0].toLowerCase()];
5756         if(typeof ce == "object"){
5757             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5758         }else{
5759             return true;
5760         }
5761     },
5762
5763     // private
5764     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5765
5766     /**
5767      * Appends an event handler to this component
5768      * @param {String}   eventName The type of event to listen for
5769      * @param {Function} handler The method the event invokes
5770      * @param {Object}   scope (optional) The scope in which to execute the handler
5771      * function. The handler function's "this" context.
5772      * @param {Object}   options (optional) An object containing handler configuration
5773      * properties. This may contain any of the following properties:<ul>
5774      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5775      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5776      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5777      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5778      * by the specified number of milliseconds. If the event fires again within that time, the original
5779      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5780      * </ul><br>
5781      * <p>
5782      * <b>Combining Options</b><br>
5783      * Using the options argument, it is possible to combine different types of listeners:<br>
5784      * <br>
5785      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5786                 <pre><code>
5787                 el.on('click', this.onClick, this, {
5788                         single: true,
5789                 delay: 100,
5790                 forumId: 4
5791                 });
5792                 </code></pre>
5793      * <p>
5794      * <b>Attaching multiple handlers in 1 call</b><br>
5795      * The method also allows for a single argument to be passed which is a config object containing properties
5796      * which specify multiple handlers.
5797      * <pre><code>
5798                 el.on({
5799                         'click': {
5800                         fn: this.onClick,
5801                         scope: this,
5802                         delay: 100
5803                 }, 
5804                 'mouseover': {
5805                         fn: this.onMouseOver,
5806                         scope: this
5807                 },
5808                 'mouseout': {
5809                         fn: this.onMouseOut,
5810                         scope: this
5811                 }
5812                 });
5813                 </code></pre>
5814      * <p>
5815      * Or a shorthand syntax which passes the same scope object to all handlers:
5816         <pre><code>
5817                 el.on({
5818                         'click': this.onClick,
5819                 'mouseover': this.onMouseOver,
5820                 'mouseout': this.onMouseOut,
5821                 scope: this
5822                 });
5823                 </code></pre>
5824      */
5825     addListener : function(eventName, fn, scope, o){
5826         if(typeof eventName == "object"){
5827             o = eventName;
5828             for(var e in o){
5829                 if(this.filterOptRe.test(e)){
5830                     continue;
5831                 }
5832                 if(typeof o[e] == "function"){
5833                     // shared options
5834                     this.addListener(e, o[e], o.scope,  o);
5835                 }else{
5836                     // individual options
5837                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5838                 }
5839             }
5840             return;
5841         }
5842         o = (!o || typeof o == "boolean") ? {} : o;
5843         eventName = eventName.toLowerCase();
5844         var ce = this.events[eventName] || true;
5845         if(typeof ce == "boolean"){
5846             ce = new Roo.util.Event(this, eventName);
5847             this.events[eventName] = ce;
5848         }
5849         ce.addListener(fn, scope, o);
5850     },
5851
5852     /**
5853      * Removes a listener
5854      * @param {String}   eventName     The type of event to listen for
5855      * @param {Function} handler        The handler to remove
5856      * @param {Object}   scope  (optional) The scope (this object) for the handler
5857      */
5858     removeListener : function(eventName, fn, scope){
5859         var ce = this.events[eventName.toLowerCase()];
5860         if(typeof ce == "object"){
5861             ce.removeListener(fn, scope);
5862         }
5863     },
5864
5865     /**
5866      * Removes all listeners for this object
5867      */
5868     purgeListeners : function(){
5869         for(var evt in this.events){
5870             if(typeof this.events[evt] == "object"){
5871                  this.events[evt].clearListeners();
5872             }
5873         }
5874     },
5875
5876     relayEvents : function(o, events){
5877         var createHandler = function(ename){
5878             return function(){
5879                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5880             };
5881         };
5882         for(var i = 0, len = events.length; i < len; i++){
5883             var ename = events[i];
5884             if(!this.events[ename]){ this.events[ename] = true; };
5885             o.on(ename, createHandler(ename), this);
5886         }
5887     },
5888
5889     /**
5890      * Used to define events on this Observable
5891      * @param {Object} object The object with the events defined
5892      */
5893     addEvents : function(o){
5894         if(!this.events){
5895             this.events = {};
5896         }
5897         Roo.applyIf(this.events, o);
5898     },
5899
5900     /**
5901      * Checks to see if this object has any listeners for a specified event
5902      * @param {String} eventName The name of the event to check for
5903      * @return {Boolean} True if the event is being listened for, else false
5904      */
5905     hasListener : function(eventName){
5906         var e = this.events[eventName];
5907         return typeof e == "object" && e.listeners.length > 0;
5908     }
5909 };
5910 /**
5911  * Appends an event handler to this element (shorthand for addListener)
5912  * @param {String}   eventName     The type of event to listen for
5913  * @param {Function} handler        The method the event invokes
5914  * @param {Object}   scope (optional) The scope in which to execute the handler
5915  * function. The handler function's "this" context.
5916  * @param {Object}   options  (optional)
5917  * @method
5918  */
5919 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5920 /**
5921  * Removes a listener (shorthand for removeListener)
5922  * @param {String}   eventName     The type of event to listen for
5923  * @param {Function} handler        The handler to remove
5924  * @param {Object}   scope  (optional) The scope (this object) for the handler
5925  * @method
5926  */
5927 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5928
5929 /**
5930  * Starts capture on the specified Observable. All events will be passed
5931  * to the supplied function with the event name + standard signature of the event
5932  * <b>before</b> the event is fired. If the supplied function returns false,
5933  * the event will not fire.
5934  * @param {Observable} o The Observable to capture
5935  * @param {Function} fn The function to call
5936  * @param {Object} scope (optional) The scope (this object) for the fn
5937  * @static
5938  */
5939 Roo.util.Observable.capture = function(o, fn, scope){
5940     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5941 };
5942
5943 /**
5944  * Removes <b>all</b> added captures from the Observable.
5945  * @param {Observable} o The Observable to release
5946  * @static
5947  */
5948 Roo.util.Observable.releaseCapture = function(o){
5949     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5950 };
5951
5952 (function(){
5953
5954     var createBuffered = function(h, o, scope){
5955         var task = new Roo.util.DelayedTask();
5956         return function(){
5957             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5958         };
5959     };
5960
5961     var createSingle = function(h, e, fn, scope){
5962         return function(){
5963             e.removeListener(fn, scope);
5964             return h.apply(scope, arguments);
5965         };
5966     };
5967
5968     var createDelayed = function(h, o, scope){
5969         return function(){
5970             var args = Array.prototype.slice.call(arguments, 0);
5971             setTimeout(function(){
5972                 h.apply(scope, args);
5973             }, o.delay || 10);
5974         };
5975     };
5976
5977     Roo.util.Event = function(obj, name){
5978         this.name = name;
5979         this.obj = obj;
5980         this.listeners = [];
5981     };
5982
5983     Roo.util.Event.prototype = {
5984         addListener : function(fn, scope, options){
5985             var o = options || {};
5986             scope = scope || this.obj;
5987             if(!this.isListening(fn, scope)){
5988                 var l = {fn: fn, scope: scope, options: o};
5989                 var h = fn;
5990                 if(o.delay){
5991                     h = createDelayed(h, o, scope);
5992                 }
5993                 if(o.single){
5994                     h = createSingle(h, this, fn, scope);
5995                 }
5996                 if(o.buffer){
5997                     h = createBuffered(h, o, scope);
5998                 }
5999                 l.fireFn = h;
6000                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6001                     this.listeners.push(l);
6002                 }else{
6003                     this.listeners = this.listeners.slice(0);
6004                     this.listeners.push(l);
6005                 }
6006             }
6007         },
6008
6009         findListener : function(fn, scope){
6010             scope = scope || this.obj;
6011             var ls = this.listeners;
6012             for(var i = 0, len = ls.length; i < len; i++){
6013                 var l = ls[i];
6014                 if(l.fn == fn && l.scope == scope){
6015                     return i;
6016                 }
6017             }
6018             return -1;
6019         },
6020
6021         isListening : function(fn, scope){
6022             return this.findListener(fn, scope) != -1;
6023         },
6024
6025         removeListener : function(fn, scope){
6026             var index;
6027             if((index = this.findListener(fn, scope)) != -1){
6028                 if(!this.firing){
6029                     this.listeners.splice(index, 1);
6030                 }else{
6031                     this.listeners = this.listeners.slice(0);
6032                     this.listeners.splice(index, 1);
6033                 }
6034                 return true;
6035             }
6036             return false;
6037         },
6038
6039         clearListeners : function(){
6040             this.listeners = [];
6041         },
6042
6043         fire : function(){
6044             var ls = this.listeners, scope, len = ls.length;
6045             if(len > 0){
6046                 this.firing = true;
6047                 var args = Array.prototype.slice.call(arguments, 0);
6048                 for(var i = 0; i < len; i++){
6049                     var l = ls[i];
6050                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6051                         this.firing = false;
6052                         return false;
6053                     }
6054                 }
6055                 this.firing = false;
6056             }
6057             return true;
6058         }
6059     };
6060 })();/*
6061  * RooJS Library 
6062  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6063  *
6064  * Licence LGPL 
6065  *
6066  */
6067  
6068 /**
6069  * @class Roo.Document
6070  * @extends Roo.util.Observable
6071  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6072  * 
6073  * @param {Object} config the methods and properties of the 'base' class for the application.
6074  * 
6075  *  Generic Page handler - implement this to start your app..
6076  * 
6077  * eg.
6078  *  MyProject = new Roo.Document({
6079         events : {
6080             'load' : true // your events..
6081         },
6082         listeners : {
6083             'ready' : function() {
6084                 // fired on Roo.onReady()
6085             }
6086         }
6087  * 
6088  */
6089 Roo.Document = function(cfg) {
6090      
6091     this.addEvents({ 
6092         'ready' : true
6093     });
6094     Roo.util.Observable.call(this,cfg);
6095     
6096     var _this = this;
6097     
6098     Roo.onReady(function() {
6099         _this.fireEvent('ready');
6100     },null,false);
6101     
6102     
6103 }
6104
6105 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6106  * Based on:
6107  * Ext JS Library 1.1.1
6108  * Copyright(c) 2006-2007, Ext JS, LLC.
6109  *
6110  * Originally Released Under LGPL - original licence link has changed is not relivant.
6111  *
6112  * Fork - LGPL
6113  * <script type="text/javascript">
6114  */
6115
6116 /**
6117  * @class Roo.EventManager
6118  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6119  * several useful events directly.
6120  * See {@link Roo.EventObject} for more details on normalized event objects.
6121  * @singleton
6122  */
6123 Roo.EventManager = function(){
6124     var docReadyEvent, docReadyProcId, docReadyState = false;
6125     var resizeEvent, resizeTask, textEvent, textSize;
6126     var E = Roo.lib.Event;
6127     var D = Roo.lib.Dom;
6128
6129     
6130     
6131
6132     var fireDocReady = function(){
6133         if(!docReadyState){
6134             docReadyState = true;
6135             Roo.isReady = true;
6136             if(docReadyProcId){
6137                 clearInterval(docReadyProcId);
6138             }
6139             if(Roo.isGecko || Roo.isOpera) {
6140                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6141             }
6142             if(Roo.isIE){
6143                 var defer = document.getElementById("ie-deferred-loader");
6144                 if(defer){
6145                     defer.onreadystatechange = null;
6146                     defer.parentNode.removeChild(defer);
6147                 }
6148             }
6149             if(docReadyEvent){
6150                 docReadyEvent.fire();
6151                 docReadyEvent.clearListeners();
6152             }
6153         }
6154     };
6155     
6156     var initDocReady = function(){
6157         docReadyEvent = new Roo.util.Event();
6158         if(Roo.isGecko || Roo.isOpera) {
6159             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6160         }else if(Roo.isIE){
6161             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6162             var defer = document.getElementById("ie-deferred-loader");
6163             defer.onreadystatechange = function(){
6164                 if(this.readyState == "complete"){
6165                     fireDocReady();
6166                 }
6167             };
6168         }else if(Roo.isSafari){ 
6169             docReadyProcId = setInterval(function(){
6170                 var rs = document.readyState;
6171                 if(rs == "complete") {
6172                     fireDocReady();     
6173                  }
6174             }, 10);
6175         }
6176         // no matter what, make sure it fires on load
6177         E.on(window, "load", fireDocReady);
6178     };
6179
6180     var createBuffered = function(h, o){
6181         var task = new Roo.util.DelayedTask(h);
6182         return function(e){
6183             // create new event object impl so new events don't wipe out properties
6184             e = new Roo.EventObjectImpl(e);
6185             task.delay(o.buffer, h, null, [e]);
6186         };
6187     };
6188
6189     var createSingle = function(h, el, ename, fn){
6190         return function(e){
6191             Roo.EventManager.removeListener(el, ename, fn);
6192             h(e);
6193         };
6194     };
6195
6196     var createDelayed = function(h, o){
6197         return function(e){
6198             // create new event object impl so new events don't wipe out properties
6199             e = new Roo.EventObjectImpl(e);
6200             setTimeout(function(){
6201                 h(e);
6202             }, o.delay || 10);
6203         };
6204     };
6205     var transitionEndVal = false;
6206     
6207     var transitionEnd = function()
6208     {
6209         if (transitionEndVal) {
6210             return transitionEndVal;
6211         }
6212         var el = document.createElement('div');
6213
6214         var transEndEventNames = {
6215             WebkitTransition : 'webkitTransitionEnd',
6216             MozTransition    : 'transitionend',
6217             OTransition      : 'oTransitionEnd otransitionend',
6218             transition       : 'transitionend'
6219         };
6220     
6221         for (var name in transEndEventNames) {
6222             if (el.style[name] !== undefined) {
6223                 transitionEndVal = transEndEventNames[name];
6224                 return  transitionEndVal ;
6225             }
6226         }
6227     }
6228     
6229
6230     var listen = function(element, ename, opt, fn, scope){
6231         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6232         fn = fn || o.fn; scope = scope || o.scope;
6233         var el = Roo.getDom(element);
6234         
6235         
6236         if(!el){
6237             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6238         }
6239         
6240         if (ename == 'transitionend') {
6241             ename = transitionEnd();
6242         }
6243         var h = function(e){
6244             e = Roo.EventObject.setEvent(e);
6245             var t;
6246             if(o.delegate){
6247                 t = e.getTarget(o.delegate, el);
6248                 if(!t){
6249                     return;
6250                 }
6251             }else{
6252                 t = e.target;
6253             }
6254             if(o.stopEvent === true){
6255                 e.stopEvent();
6256             }
6257             if(o.preventDefault === true){
6258                e.preventDefault();
6259             }
6260             if(o.stopPropagation === true){
6261                 e.stopPropagation();
6262             }
6263
6264             if(o.normalized === false){
6265                 e = e.browserEvent;
6266             }
6267
6268             fn.call(scope || el, e, t, o);
6269         };
6270         if(o.delay){
6271             h = createDelayed(h, o);
6272         }
6273         if(o.single){
6274             h = createSingle(h, el, ename, fn);
6275         }
6276         if(o.buffer){
6277             h = createBuffered(h, o);
6278         }
6279         fn._handlers = fn._handlers || [];
6280         
6281         
6282         fn._handlers.push([Roo.id(el), ename, h]);
6283         
6284         
6285          
6286         E.on(el, ename, h);
6287         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6288             el.addEventListener("DOMMouseScroll", h, false);
6289             E.on(window, 'unload', function(){
6290                 el.removeEventListener("DOMMouseScroll", h, false);
6291             });
6292         }
6293         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6294             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6295         }
6296         return h;
6297     };
6298
6299     var stopListening = function(el, ename, fn){
6300         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6301         if(hds){
6302             for(var i = 0, len = hds.length; i < len; i++){
6303                 var h = hds[i];
6304                 if(h[0] == id && h[1] == ename){
6305                     hd = h[2];
6306                     hds.splice(i, 1);
6307                     break;
6308                 }
6309             }
6310         }
6311         E.un(el, ename, hd);
6312         el = Roo.getDom(el);
6313         if(ename == "mousewheel" && el.addEventListener){
6314             el.removeEventListener("DOMMouseScroll", hd, false);
6315         }
6316         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6317             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6318         }
6319     };
6320
6321     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6322     
6323     var pub = {
6324         
6325         
6326         /** 
6327          * Fix for doc tools
6328          * @scope Roo.EventManager
6329          */
6330         
6331         
6332         /** 
6333          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6334          * object with a Roo.EventObject
6335          * @param {Function} fn        The method the event invokes
6336          * @param {Object}   scope    An object that becomes the scope of the handler
6337          * @param {boolean}  override If true, the obj passed in becomes
6338          *                             the execution scope of the listener
6339          * @return {Function} The wrapped function
6340          * @deprecated
6341          */
6342         wrap : function(fn, scope, override){
6343             return function(e){
6344                 Roo.EventObject.setEvent(e);
6345                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6346             };
6347         },
6348         
6349         /**
6350      * Appends an event handler to an element (shorthand for addListener)
6351      * @param {String/HTMLElement}   element        The html element or id to assign the
6352      * @param {String}   eventName The type of event to listen for
6353      * @param {Function} handler The method the event invokes
6354      * @param {Object}   scope (optional) The scope in which to execute the handler
6355      * function. The handler function's "this" context.
6356      * @param {Object}   options (optional) An object containing handler configuration
6357      * properties. This may contain any of the following properties:<ul>
6358      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6359      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6360      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6361      * <li>preventDefault {Boolean} True to prevent the default action</li>
6362      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6363      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6364      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6365      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6366      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6367      * by the specified number of milliseconds. If the event fires again within that time, the original
6368      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6369      * </ul><br>
6370      * <p>
6371      * <b>Combining Options</b><br>
6372      * Using the options argument, it is possible to combine different types of listeners:<br>
6373      * <br>
6374      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6375      * Code:<pre><code>
6376 el.on('click', this.onClick, this, {
6377     single: true,
6378     delay: 100,
6379     stopEvent : true,
6380     forumId: 4
6381 });</code></pre>
6382      * <p>
6383      * <b>Attaching multiple handlers in 1 call</b><br>
6384       * The method also allows for a single argument to be passed which is a config object containing properties
6385      * which specify multiple handlers.
6386      * <p>
6387      * Code:<pre><code>
6388 el.on({
6389     'click' : {
6390         fn: this.onClick
6391         scope: this,
6392         delay: 100
6393     },
6394     'mouseover' : {
6395         fn: this.onMouseOver
6396         scope: this
6397     },
6398     'mouseout' : {
6399         fn: this.onMouseOut
6400         scope: this
6401     }
6402 });</code></pre>
6403      * <p>
6404      * Or a shorthand syntax:<br>
6405      * Code:<pre><code>
6406 el.on({
6407     'click' : this.onClick,
6408     'mouseover' : this.onMouseOver,
6409     'mouseout' : this.onMouseOut
6410     scope: this
6411 });</code></pre>
6412      */
6413         addListener : function(element, eventName, fn, scope, options){
6414             if(typeof eventName == "object"){
6415                 var o = eventName;
6416                 for(var e in o){
6417                     if(propRe.test(e)){
6418                         continue;
6419                     }
6420                     if(typeof o[e] == "function"){
6421                         // shared options
6422                         listen(element, e, o, o[e], o.scope);
6423                     }else{
6424                         // individual options
6425                         listen(element, e, o[e]);
6426                     }
6427                 }
6428                 return;
6429             }
6430             return listen(element, eventName, options, fn, scope);
6431         },
6432         
6433         /**
6434          * Removes an event handler
6435          *
6436          * @param {String/HTMLElement}   element        The id or html element to remove the 
6437          *                             event from
6438          * @param {String}   eventName     The type of event
6439          * @param {Function} fn
6440          * @return {Boolean} True if a listener was actually removed
6441          */
6442         removeListener : function(element, eventName, fn){
6443             return stopListening(element, eventName, fn);
6444         },
6445         
6446         /**
6447          * Fires when the document is ready (before onload and before images are loaded). Can be 
6448          * accessed shorthanded Roo.onReady().
6449          * @param {Function} fn        The method the event invokes
6450          * @param {Object}   scope    An  object that becomes the scope of the handler
6451          * @param {boolean}  options
6452          */
6453         onDocumentReady : function(fn, scope, options){
6454             if(docReadyState){ // if it already fired
6455                 docReadyEvent.addListener(fn, scope, options);
6456                 docReadyEvent.fire();
6457                 docReadyEvent.clearListeners();
6458                 return;
6459             }
6460             if(!docReadyEvent){
6461                 initDocReady();
6462             }
6463             docReadyEvent.addListener(fn, scope, options);
6464         },
6465         
6466         /**
6467          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6468          * @param {Function} fn        The method the event invokes
6469          * @param {Object}   scope    An object that becomes the scope of the handler
6470          * @param {boolean}  options
6471          */
6472         onWindowResize : function(fn, scope, options){
6473             if(!resizeEvent){
6474                 resizeEvent = new Roo.util.Event();
6475                 resizeTask = new Roo.util.DelayedTask(function(){
6476                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6477                 });
6478                 E.on(window, "resize", function(){
6479                     if(Roo.isIE){
6480                         resizeTask.delay(50);
6481                     }else{
6482                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6483                     }
6484                 });
6485             }
6486             resizeEvent.addListener(fn, scope, options);
6487         },
6488
6489         /**
6490          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6491          * @param {Function} fn        The method the event invokes
6492          * @param {Object}   scope    An object that becomes the scope of the handler
6493          * @param {boolean}  options
6494          */
6495         onTextResize : function(fn, scope, options){
6496             if(!textEvent){
6497                 textEvent = new Roo.util.Event();
6498                 var textEl = new Roo.Element(document.createElement('div'));
6499                 textEl.dom.className = 'x-text-resize';
6500                 textEl.dom.innerHTML = 'X';
6501                 textEl.appendTo(document.body);
6502                 textSize = textEl.dom.offsetHeight;
6503                 setInterval(function(){
6504                     if(textEl.dom.offsetHeight != textSize){
6505                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6506                     }
6507                 }, this.textResizeInterval);
6508             }
6509             textEvent.addListener(fn, scope, options);
6510         },
6511
6512         /**
6513          * Removes the passed window resize listener.
6514          * @param {Function} fn        The method the event invokes
6515          * @param {Object}   scope    The scope of handler
6516          */
6517         removeResizeListener : function(fn, scope){
6518             if(resizeEvent){
6519                 resizeEvent.removeListener(fn, scope);
6520             }
6521         },
6522
6523         // private
6524         fireResize : function(){
6525             if(resizeEvent){
6526                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6527             }   
6528         },
6529         /**
6530          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6531          */
6532         ieDeferSrc : false,
6533         /**
6534          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6535          */
6536         textResizeInterval : 50
6537     };
6538     
6539     /**
6540      * Fix for doc tools
6541      * @scopeAlias pub=Roo.EventManager
6542      */
6543     
6544      /**
6545      * Appends an event handler to an element (shorthand for addListener)
6546      * @param {String/HTMLElement}   element        The html element or id to assign the
6547      * @param {String}   eventName The type of event to listen for
6548      * @param {Function} handler The method the event invokes
6549      * @param {Object}   scope (optional) The scope in which to execute the handler
6550      * function. The handler function's "this" context.
6551      * @param {Object}   options (optional) An object containing handler configuration
6552      * properties. This may contain any of the following properties:<ul>
6553      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6554      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6555      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6556      * <li>preventDefault {Boolean} True to prevent the default action</li>
6557      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6558      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6559      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6560      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6561      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6562      * by the specified number of milliseconds. If the event fires again within that time, the original
6563      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6564      * </ul><br>
6565      * <p>
6566      * <b>Combining Options</b><br>
6567      * Using the options argument, it is possible to combine different types of listeners:<br>
6568      * <br>
6569      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6570      * Code:<pre><code>
6571 el.on('click', this.onClick, this, {
6572     single: true,
6573     delay: 100,
6574     stopEvent : true,
6575     forumId: 4
6576 });</code></pre>
6577      * <p>
6578      * <b>Attaching multiple handlers in 1 call</b><br>
6579       * The method also allows for a single argument to be passed which is a config object containing properties
6580      * which specify multiple handlers.
6581      * <p>
6582      * Code:<pre><code>
6583 el.on({
6584     'click' : {
6585         fn: this.onClick
6586         scope: this,
6587         delay: 100
6588     },
6589     'mouseover' : {
6590         fn: this.onMouseOver
6591         scope: this
6592     },
6593     'mouseout' : {
6594         fn: this.onMouseOut
6595         scope: this
6596     }
6597 });</code></pre>
6598      * <p>
6599      * Or a shorthand syntax:<br>
6600      * Code:<pre><code>
6601 el.on({
6602     'click' : this.onClick,
6603     'mouseover' : this.onMouseOver,
6604     'mouseout' : this.onMouseOut
6605     scope: this
6606 });</code></pre>
6607      */
6608     pub.on = pub.addListener;
6609     pub.un = pub.removeListener;
6610
6611     pub.stoppedMouseDownEvent = new Roo.util.Event();
6612     return pub;
6613 }();
6614 /**
6615   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6616   * @param {Function} fn        The method the event invokes
6617   * @param {Object}   scope    An  object that becomes the scope of the handler
6618   * @param {boolean}  override If true, the obj passed in becomes
6619   *                             the execution scope of the listener
6620   * @member Roo
6621   * @method onReady
6622  */
6623 Roo.onReady = Roo.EventManager.onDocumentReady;
6624
6625 Roo.onReady(function(){
6626     var bd = Roo.get(document.body);
6627     if(!bd){ return; }
6628
6629     var cls = [
6630             Roo.isIE ? "roo-ie"
6631             : Roo.isGecko ? "roo-gecko"
6632             : Roo.isOpera ? "roo-opera"
6633             : Roo.isSafari ? "roo-safari" : ""];
6634
6635     if(Roo.isMac){
6636         cls.push("roo-mac");
6637     }
6638     if(Roo.isLinux){
6639         cls.push("roo-linux");
6640     }
6641     if(Roo.isIOS){
6642         cls.push("roo-ios");
6643     }
6644     if(Roo.isTouch){
6645         cls.push("roo-touch");
6646     }
6647     if(Roo.isBorderBox){
6648         cls.push('roo-border-box');
6649     }
6650     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6651         var p = bd.dom.parentNode;
6652         if(p){
6653             p.className += ' roo-strict';
6654         }
6655     }
6656     bd.addClass(cls.join(' '));
6657 });
6658
6659 /**
6660  * @class Roo.EventObject
6661  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6662  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6663  * Example:
6664  * <pre><code>
6665  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6666     e.preventDefault();
6667     var target = e.getTarget();
6668     ...
6669  }
6670  var myDiv = Roo.get("myDiv");
6671  myDiv.on("click", handleClick);
6672  //or
6673  Roo.EventManager.on("myDiv", 'click', handleClick);
6674  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6675  </code></pre>
6676  * @singleton
6677  */
6678 Roo.EventObject = function(){
6679     
6680     var E = Roo.lib.Event;
6681     
6682     // safari keypress events for special keys return bad keycodes
6683     var safariKeys = {
6684         63234 : 37, // left
6685         63235 : 39, // right
6686         63232 : 38, // up
6687         63233 : 40, // down
6688         63276 : 33, // page up
6689         63277 : 34, // page down
6690         63272 : 46, // delete
6691         63273 : 36, // home
6692         63275 : 35  // end
6693     };
6694
6695     // normalize button clicks
6696     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6697                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6698
6699     Roo.EventObjectImpl = function(e){
6700         if(e){
6701             this.setEvent(e.browserEvent || e);
6702         }
6703     };
6704     Roo.EventObjectImpl.prototype = {
6705         /**
6706          * Used to fix doc tools.
6707          * @scope Roo.EventObject.prototype
6708          */
6709             
6710
6711         
6712         
6713         /** The normal browser event */
6714         browserEvent : null,
6715         /** The button pressed in a mouse event */
6716         button : -1,
6717         /** True if the shift key was down during the event */
6718         shiftKey : false,
6719         /** True if the control key was down during the event */
6720         ctrlKey : false,
6721         /** True if the alt key was down during the event */
6722         altKey : false,
6723
6724         /** Key constant 
6725         * @type Number */
6726         BACKSPACE : 8,
6727         /** Key constant 
6728         * @type Number */
6729         TAB : 9,
6730         /** Key constant 
6731         * @type Number */
6732         RETURN : 13,
6733         /** Key constant 
6734         * @type Number */
6735         ENTER : 13,
6736         /** Key constant 
6737         * @type Number */
6738         SHIFT : 16,
6739         /** Key constant 
6740         * @type Number */
6741         CONTROL : 17,
6742         /** Key constant 
6743         * @type Number */
6744         ESC : 27,
6745         /** Key constant 
6746         * @type Number */
6747         SPACE : 32,
6748         /** Key constant 
6749         * @type Number */
6750         PAGEUP : 33,
6751         /** Key constant 
6752         * @type Number */
6753         PAGEDOWN : 34,
6754         /** Key constant 
6755         * @type Number */
6756         END : 35,
6757         /** Key constant 
6758         * @type Number */
6759         HOME : 36,
6760         /** Key constant 
6761         * @type Number */
6762         LEFT : 37,
6763         /** Key constant 
6764         * @type Number */
6765         UP : 38,
6766         /** Key constant 
6767         * @type Number */
6768         RIGHT : 39,
6769         /** Key constant 
6770         * @type Number */
6771         DOWN : 40,
6772         /** Key constant 
6773         * @type Number */
6774         DELETE : 46,
6775         /** Key constant 
6776         * @type Number */
6777         F5 : 116,
6778
6779            /** @private */
6780         setEvent : function(e){
6781             if(e == this || (e && e.browserEvent)){ // already wrapped
6782                 return e;
6783             }
6784             this.browserEvent = e;
6785             if(e){
6786                 // normalize buttons
6787                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6788                 if(e.type == 'click' && this.button == -1){
6789                     this.button = 0;
6790                 }
6791                 this.type = e.type;
6792                 this.shiftKey = e.shiftKey;
6793                 // mac metaKey behaves like ctrlKey
6794                 this.ctrlKey = e.ctrlKey || e.metaKey;
6795                 this.altKey = e.altKey;
6796                 // in getKey these will be normalized for the mac
6797                 this.keyCode = e.keyCode;
6798                 // keyup warnings on firefox.
6799                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6800                 // cache the target for the delayed and or buffered events
6801                 this.target = E.getTarget(e);
6802                 // same for XY
6803                 this.xy = E.getXY(e);
6804             }else{
6805                 this.button = -1;
6806                 this.shiftKey = false;
6807                 this.ctrlKey = false;
6808                 this.altKey = false;
6809                 this.keyCode = 0;
6810                 this.charCode =0;
6811                 this.target = null;
6812                 this.xy = [0, 0];
6813             }
6814             return this;
6815         },
6816
6817         /**
6818          * Stop the event (preventDefault and stopPropagation)
6819          */
6820         stopEvent : function(){
6821             if(this.browserEvent){
6822                 if(this.browserEvent.type == 'mousedown'){
6823                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6824                 }
6825                 E.stopEvent(this.browserEvent);
6826             }
6827         },
6828
6829         /**
6830          * Prevents the browsers default handling of the event.
6831          */
6832         preventDefault : function(){
6833             if(this.browserEvent){
6834                 E.preventDefault(this.browserEvent);
6835             }
6836         },
6837
6838         /** @private */
6839         isNavKeyPress : function(){
6840             var k = this.keyCode;
6841             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6842             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6843         },
6844
6845         isSpecialKey : function(){
6846             var k = this.keyCode;
6847             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6848             (k == 16) || (k == 17) ||
6849             (k >= 18 && k <= 20) ||
6850             (k >= 33 && k <= 35) ||
6851             (k >= 36 && k <= 39) ||
6852             (k >= 44 && k <= 45);
6853         },
6854         /**
6855          * Cancels bubbling of the event.
6856          */
6857         stopPropagation : function(){
6858             if(this.browserEvent){
6859                 if(this.type == 'mousedown'){
6860                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6861                 }
6862                 E.stopPropagation(this.browserEvent);
6863             }
6864         },
6865
6866         /**
6867          * Gets the key code for the event.
6868          * @return {Number}
6869          */
6870         getCharCode : function(){
6871             return this.charCode || this.keyCode;
6872         },
6873
6874         /**
6875          * Returns a normalized keyCode for the event.
6876          * @return {Number} The key code
6877          */
6878         getKey : function(){
6879             var k = this.keyCode || this.charCode;
6880             return Roo.isSafari ? (safariKeys[k] || k) : k;
6881         },
6882
6883         /**
6884          * Gets the x coordinate of the event.
6885          * @return {Number}
6886          */
6887         getPageX : function(){
6888             return this.xy[0];
6889         },
6890
6891         /**
6892          * Gets the y coordinate of the event.
6893          * @return {Number}
6894          */
6895         getPageY : function(){
6896             return this.xy[1];
6897         },
6898
6899         /**
6900          * Gets the time of the event.
6901          * @return {Number}
6902          */
6903         getTime : function(){
6904             if(this.browserEvent){
6905                 return E.getTime(this.browserEvent);
6906             }
6907             return null;
6908         },
6909
6910         /**
6911          * Gets the page coordinates of the event.
6912          * @return {Array} The xy values like [x, y]
6913          */
6914         getXY : function(){
6915             return this.xy;
6916         },
6917
6918         /**
6919          * Gets the target for the event.
6920          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6921          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6922                 search as a number or element (defaults to 10 || document.body)
6923          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6924          * @return {HTMLelement}
6925          */
6926         getTarget : function(selector, maxDepth, returnEl){
6927             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6928         },
6929         /**
6930          * Gets the related target.
6931          * @return {HTMLElement}
6932          */
6933         getRelatedTarget : function(){
6934             if(this.browserEvent){
6935                 return E.getRelatedTarget(this.browserEvent);
6936             }
6937             return null;
6938         },
6939
6940         /**
6941          * Normalizes mouse wheel delta across browsers
6942          * @return {Number} The delta
6943          */
6944         getWheelDelta : function(){
6945             var e = this.browserEvent;
6946             var delta = 0;
6947             if(e.wheelDelta){ /* IE/Opera. */
6948                 delta = e.wheelDelta/120;
6949             }else if(e.detail){ /* Mozilla case. */
6950                 delta = -e.detail/3;
6951             }
6952             return delta;
6953         },
6954
6955         /**
6956          * Returns true if the control, meta, shift or alt key was pressed during this event.
6957          * @return {Boolean}
6958          */
6959         hasModifier : function(){
6960             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6961         },
6962
6963         /**
6964          * Returns true if the target of this event equals el or is a child of el
6965          * @param {String/HTMLElement/Element} el
6966          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6967          * @return {Boolean}
6968          */
6969         within : function(el, related){
6970             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6971             return t && Roo.fly(el).contains(t);
6972         },
6973
6974         getPoint : function(){
6975             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6976         }
6977     };
6978
6979     return new Roo.EventObjectImpl();
6980 }();
6981             
6982     /*
6983  * Based on:
6984  * Ext JS Library 1.1.1
6985  * Copyright(c) 2006-2007, Ext JS, LLC.
6986  *
6987  * Originally Released Under LGPL - original licence link has changed is not relivant.
6988  *
6989  * Fork - LGPL
6990  * <script type="text/javascript">
6991  */
6992
6993  
6994 // was in Composite Element!??!?!
6995  
6996 (function(){
6997     var D = Roo.lib.Dom;
6998     var E = Roo.lib.Event;
6999     var A = Roo.lib.Anim;
7000
7001     // local style camelizing for speed
7002     var propCache = {};
7003     var camelRe = /(-[a-z])/gi;
7004     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7005     var view = document.defaultView;
7006
7007 /**
7008  * @class Roo.Element
7009  * Represents an Element in the DOM.<br><br>
7010  * Usage:<br>
7011 <pre><code>
7012 var el = Roo.get("my-div");
7013
7014 // or with getEl
7015 var el = getEl("my-div");
7016
7017 // or with a DOM element
7018 var el = Roo.get(myDivElement);
7019 </code></pre>
7020  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7021  * each call instead of constructing a new one.<br><br>
7022  * <b>Animations</b><br />
7023  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7024  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7025 <pre>
7026 Option    Default   Description
7027 --------- --------  ---------------------------------------------
7028 duration  .35       The duration of the animation in seconds
7029 easing    easeOut   The YUI easing method
7030 callback  none      A function to execute when the anim completes
7031 scope     this      The scope (this) of the callback function
7032 </pre>
7033 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7034 * manipulate the animation. Here's an example:
7035 <pre><code>
7036 var el = Roo.get("my-div");
7037
7038 // no animation
7039 el.setWidth(100);
7040
7041 // default animation
7042 el.setWidth(100, true);
7043
7044 // animation with some options set
7045 el.setWidth(100, {
7046     duration: 1,
7047     callback: this.foo,
7048     scope: this
7049 });
7050
7051 // using the "anim" property to get the Anim object
7052 var opt = {
7053     duration: 1,
7054     callback: this.foo,
7055     scope: this
7056 };
7057 el.setWidth(100, opt);
7058 ...
7059 if(opt.anim.isAnimated()){
7060     opt.anim.stop();
7061 }
7062 </code></pre>
7063 * <b> Composite (Collections of) Elements</b><br />
7064  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7065  * @constructor Create a new Element directly.
7066  * @param {String/HTMLElement} element
7067  * @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).
7068  */
7069     Roo.Element = function(element, forceNew){
7070         var dom = typeof element == "string" ?
7071                 document.getElementById(element) : element;
7072         if(!dom){ // invalid id/element
7073             return null;
7074         }
7075         var id = dom.id;
7076         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7077             return Roo.Element.cache[id];
7078         }
7079
7080         /**
7081          * The DOM element
7082          * @type HTMLElement
7083          */
7084         this.dom = dom;
7085
7086         /**
7087          * The DOM element ID
7088          * @type String
7089          */
7090         this.id = id || Roo.id(dom);
7091     };
7092
7093     var El = Roo.Element;
7094
7095     El.prototype = {
7096         /**
7097          * The element's default display mode  (defaults to "")
7098          * @type String
7099          */
7100         originalDisplay : "",
7101
7102         visibilityMode : 1,
7103         /**
7104          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7105          * @type String
7106          */
7107         defaultUnit : "px",
7108         
7109         /**
7110          * Sets the element's visibility mode. When setVisible() is called it
7111          * will use this to determine whether to set the visibility or the display property.
7112          * @param visMode Element.VISIBILITY or Element.DISPLAY
7113          * @return {Roo.Element} this
7114          */
7115         setVisibilityMode : function(visMode){
7116             this.visibilityMode = visMode;
7117             return this;
7118         },
7119         /**
7120          * Convenience method for setVisibilityMode(Element.DISPLAY)
7121          * @param {String} display (optional) What to set display to when visible
7122          * @return {Roo.Element} this
7123          */
7124         enableDisplayMode : function(display){
7125             this.setVisibilityMode(El.DISPLAY);
7126             if(typeof display != "undefined") { this.originalDisplay = display; }
7127             return this;
7128         },
7129
7130         /**
7131          * 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)
7132          * @param {String} selector The simple selector to test
7133          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7134                 search as a number or element (defaults to 10 || document.body)
7135          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7136          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7137          */
7138         findParent : function(simpleSelector, maxDepth, returnEl){
7139             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7140             maxDepth = maxDepth || 50;
7141             if(typeof maxDepth != "number"){
7142                 stopEl = Roo.getDom(maxDepth);
7143                 maxDepth = 10;
7144             }
7145             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7146                 if(dq.is(p, simpleSelector)){
7147                     return returnEl ? Roo.get(p) : p;
7148                 }
7149                 depth++;
7150                 p = p.parentNode;
7151             }
7152             return null;
7153         },
7154
7155
7156         /**
7157          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7158          * @param {String} selector The simple selector to test
7159          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7160                 search as a number or element (defaults to 10 || document.body)
7161          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7162          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7163          */
7164         findParentNode : function(simpleSelector, maxDepth, returnEl){
7165             var p = Roo.fly(this.dom.parentNode, '_internal');
7166             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7167         },
7168         
7169         /**
7170          * Looks at  the scrollable parent element
7171          */
7172         findScrollableParent : function()
7173         {
7174             var overflowRegex = /(auto|scroll)/;
7175             
7176             if(this.getStyle('position') === 'fixed'){
7177                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7178             }
7179             
7180             var excludeStaticParent = this.getStyle('position') === "absolute";
7181             
7182             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7183                 
7184                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7185                     continue;
7186                 }
7187                 
7188                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7189                     return parent;
7190                 }
7191                 
7192                 if(parent.dom.nodeName.toLowerCase() == 'body'){
7193                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7194                 }
7195             }
7196             
7197             alert('is android : ' + Roo.isAndroid);
7198             alert('is ios : ' + Roo.isIOS);
7199             
7200             if(Roo.isAndroid){
7201                 alert('Is Android');
7202                 return Roo.get(document.documentElement);
7203             }
7204             
7205             if(!Roo.isAndroid){
7206                 alert('not android');
7207             }
7208             
7209             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7210         },
7211
7212         /**
7213          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7214          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7215          * @param {String} selector The simple selector to test
7216          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7217                 search as a number or element (defaults to 10 || document.body)
7218          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7219          */
7220         up : function(simpleSelector, maxDepth){
7221             return this.findParentNode(simpleSelector, maxDepth, true);
7222         },
7223
7224
7225
7226         /**
7227          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7228          * @param {String} selector The simple selector to test
7229          * @return {Boolean} True if this element matches the selector, else false
7230          */
7231         is : function(simpleSelector){
7232             return Roo.DomQuery.is(this.dom, simpleSelector);
7233         },
7234
7235         /**
7236          * Perform animation on this element.
7237          * @param {Object} args The YUI animation control args
7238          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7239          * @param {Function} onComplete (optional) Function to call when animation completes
7240          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7241          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7242          * @return {Roo.Element} this
7243          */
7244         animate : function(args, duration, onComplete, easing, animType){
7245             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7246             return this;
7247         },
7248
7249         /*
7250          * @private Internal animation call
7251          */
7252         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7253             animType = animType || 'run';
7254             opt = opt || {};
7255             var anim = Roo.lib.Anim[animType](
7256                 this.dom, args,
7257                 (opt.duration || defaultDur) || .35,
7258                 (opt.easing || defaultEase) || 'easeOut',
7259                 function(){
7260                     Roo.callback(cb, this);
7261                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7262                 },
7263                 this
7264             );
7265             opt.anim = anim;
7266             return anim;
7267         },
7268
7269         // private legacy anim prep
7270         preanim : function(a, i){
7271             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7272         },
7273
7274         /**
7275          * Removes worthless text nodes
7276          * @param {Boolean} forceReclean (optional) By default the element
7277          * keeps track if it has been cleaned already so
7278          * you can call this over and over. However, if you update the element and
7279          * need to force a reclean, you can pass true.
7280          */
7281         clean : function(forceReclean){
7282             if(this.isCleaned && forceReclean !== true){
7283                 return this;
7284             }
7285             var ns = /\S/;
7286             var d = this.dom, n = d.firstChild, ni = -1;
7287             while(n){
7288                 var nx = n.nextSibling;
7289                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7290                     d.removeChild(n);
7291                 }else{
7292                     n.nodeIndex = ++ni;
7293                 }
7294                 n = nx;
7295             }
7296             this.isCleaned = true;
7297             return this;
7298         },
7299
7300         // private
7301         calcOffsetsTo : function(el){
7302             el = Roo.get(el);
7303             var d = el.dom;
7304             var restorePos = false;
7305             if(el.getStyle('position') == 'static'){
7306                 el.position('relative');
7307                 restorePos = true;
7308             }
7309             var x = 0, y =0;
7310             var op = this.dom;
7311             while(op && op != d && op.tagName != 'HTML'){
7312                 x+= op.offsetLeft;
7313                 y+= op.offsetTop;
7314                 op = op.offsetParent;
7315             }
7316             if(restorePos){
7317                 el.position('static');
7318             }
7319             return [x, y];
7320         },
7321
7322         /**
7323          * Scrolls this element into view within the passed container.
7324          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7325          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7326          * @return {Roo.Element} this
7327          */
7328         scrollIntoView : function(container, hscroll){
7329             var c = Roo.getDom(container) || document.body;
7330             var el = this.dom;
7331
7332             var o = this.calcOffsetsTo(c),
7333                 l = o[0],
7334                 t = o[1],
7335                 b = t+el.offsetHeight,
7336                 r = l+el.offsetWidth;
7337
7338             var ch = c.clientHeight;
7339             var ct = parseInt(c.scrollTop, 10);
7340             var cl = parseInt(c.scrollLeft, 10);
7341             var cb = ct + ch;
7342             var cr = cl + c.clientWidth;
7343
7344             if(t < ct){
7345                 c.scrollTop = t;
7346             }else if(b > cb){
7347                 c.scrollTop = b-ch;
7348             }
7349
7350             if(hscroll !== false){
7351                 if(l < cl){
7352                     c.scrollLeft = l;
7353                 }else if(r > cr){
7354                     c.scrollLeft = r-c.clientWidth;
7355                 }
7356             }
7357             return this;
7358         },
7359
7360         // private
7361         scrollChildIntoView : function(child, hscroll){
7362             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7363         },
7364
7365         /**
7366          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7367          * the new height may not be available immediately.
7368          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7369          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7370          * @param {Function} onComplete (optional) Function to call when animation completes
7371          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7372          * @return {Roo.Element} this
7373          */
7374         autoHeight : function(animate, duration, onComplete, easing){
7375             var oldHeight = this.getHeight();
7376             this.clip();
7377             this.setHeight(1); // force clipping
7378             setTimeout(function(){
7379                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7380                 if(!animate){
7381                     this.setHeight(height);
7382                     this.unclip();
7383                     if(typeof onComplete == "function"){
7384                         onComplete();
7385                     }
7386                 }else{
7387                     this.setHeight(oldHeight); // restore original height
7388                     this.setHeight(height, animate, duration, function(){
7389                         this.unclip();
7390                         if(typeof onComplete == "function") { onComplete(); }
7391                     }.createDelegate(this), easing);
7392                 }
7393             }.createDelegate(this), 0);
7394             return this;
7395         },
7396
7397         /**
7398          * Returns true if this element is an ancestor of the passed element
7399          * @param {HTMLElement/String} el The element to check
7400          * @return {Boolean} True if this element is an ancestor of el, else false
7401          */
7402         contains : function(el){
7403             if(!el){return false;}
7404             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7405         },
7406
7407         /**
7408          * Checks whether the element is currently visible using both visibility and display properties.
7409          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7410          * @return {Boolean} True if the element is currently visible, else false
7411          */
7412         isVisible : function(deep) {
7413             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7414             if(deep !== true || !vis){
7415                 return vis;
7416             }
7417             var p = this.dom.parentNode;
7418             while(p && p.tagName.toLowerCase() != "body"){
7419                 if(!Roo.fly(p, '_isVisible').isVisible()){
7420                     return false;
7421                 }
7422                 p = p.parentNode;
7423             }
7424             return true;
7425         },
7426
7427         /**
7428          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7429          * @param {String} selector The CSS selector
7430          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7431          * @return {CompositeElement/CompositeElementLite} The composite element
7432          */
7433         select : function(selector, unique){
7434             return El.select(selector, unique, this.dom);
7435         },
7436
7437         /**
7438          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7439          * @param {String} selector The CSS selector
7440          * @return {Array} An array of the matched nodes
7441          */
7442         query : function(selector, unique){
7443             return Roo.DomQuery.select(selector, this.dom);
7444         },
7445
7446         /**
7447          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7448          * @param {String} selector The CSS selector
7449          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7450          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7451          */
7452         child : function(selector, returnDom){
7453             var n = Roo.DomQuery.selectNode(selector, this.dom);
7454             return returnDom ? n : Roo.get(n);
7455         },
7456
7457         /**
7458          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7459          * @param {String} selector The CSS selector
7460          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7461          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7462          */
7463         down : function(selector, returnDom){
7464             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7465             return returnDom ? n : Roo.get(n);
7466         },
7467
7468         /**
7469          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7470          * @param {String} group The group the DD object is member of
7471          * @param {Object} config The DD config object
7472          * @param {Object} overrides An object containing methods to override/implement on the DD object
7473          * @return {Roo.dd.DD} The DD object
7474          */
7475         initDD : function(group, config, overrides){
7476             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7477             return Roo.apply(dd, overrides);
7478         },
7479
7480         /**
7481          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7482          * @param {String} group The group the DDProxy object is member of
7483          * @param {Object} config The DDProxy config object
7484          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7485          * @return {Roo.dd.DDProxy} The DDProxy object
7486          */
7487         initDDProxy : function(group, config, overrides){
7488             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7489             return Roo.apply(dd, overrides);
7490         },
7491
7492         /**
7493          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7494          * @param {String} group The group the DDTarget object is member of
7495          * @param {Object} config The DDTarget config object
7496          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7497          * @return {Roo.dd.DDTarget} The DDTarget object
7498          */
7499         initDDTarget : function(group, config, overrides){
7500             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7501             return Roo.apply(dd, overrides);
7502         },
7503
7504         /**
7505          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7506          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7507          * @param {Boolean} visible Whether the element is visible
7508          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7509          * @return {Roo.Element} this
7510          */
7511          setVisible : function(visible, animate){
7512             if(!animate || !A){
7513                 if(this.visibilityMode == El.DISPLAY){
7514                     this.setDisplayed(visible);
7515                 }else{
7516                     this.fixDisplay();
7517                     this.dom.style.visibility = visible ? "visible" : "hidden";
7518                 }
7519             }else{
7520                 // closure for composites
7521                 var dom = this.dom;
7522                 var visMode = this.visibilityMode;
7523                 if(visible){
7524                     this.setOpacity(.01);
7525                     this.setVisible(true);
7526                 }
7527                 this.anim({opacity: { to: (visible?1:0) }},
7528                       this.preanim(arguments, 1),
7529                       null, .35, 'easeIn', function(){
7530                          if(!visible){
7531                              if(visMode == El.DISPLAY){
7532                                  dom.style.display = "none";
7533                              }else{
7534                                  dom.style.visibility = "hidden";
7535                              }
7536                              Roo.get(dom).setOpacity(1);
7537                          }
7538                      });
7539             }
7540             return this;
7541         },
7542
7543         /**
7544          * Returns true if display is not "none"
7545          * @return {Boolean}
7546          */
7547         isDisplayed : function() {
7548             return this.getStyle("display") != "none";
7549         },
7550
7551         /**
7552          * Toggles the element's visibility or display, depending on visibility mode.
7553          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7554          * @return {Roo.Element} this
7555          */
7556         toggle : function(animate){
7557             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7558             return this;
7559         },
7560
7561         /**
7562          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7563          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7564          * @return {Roo.Element} this
7565          */
7566         setDisplayed : function(value) {
7567             if(typeof value == "boolean"){
7568                value = value ? this.originalDisplay : "none";
7569             }
7570             this.setStyle("display", value);
7571             return this;
7572         },
7573
7574         /**
7575          * Tries to focus the element. Any exceptions are caught and ignored.
7576          * @return {Roo.Element} this
7577          */
7578         focus : function() {
7579             try{
7580                 this.dom.focus();
7581             }catch(e){}
7582             return this;
7583         },
7584
7585         /**
7586          * Tries to blur the element. Any exceptions are caught and ignored.
7587          * @return {Roo.Element} this
7588          */
7589         blur : function() {
7590             try{
7591                 this.dom.blur();
7592             }catch(e){}
7593             return this;
7594         },
7595
7596         /**
7597          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7598          * @param {String/Array} className The CSS class to add, or an array of classes
7599          * @return {Roo.Element} this
7600          */
7601         addClass : function(className){
7602             if(className instanceof Array){
7603                 for(var i = 0, len = className.length; i < len; i++) {
7604                     this.addClass(className[i]);
7605                 }
7606             }else{
7607                 if(className && !this.hasClass(className)){
7608                     this.dom.className = this.dom.className + " " + className;
7609                 }
7610             }
7611             return this;
7612         },
7613
7614         /**
7615          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7616          * @param {String/Array} className The CSS class to add, or an array of classes
7617          * @return {Roo.Element} this
7618          */
7619         radioClass : function(className){
7620             var siblings = this.dom.parentNode.childNodes;
7621             for(var i = 0; i < siblings.length; i++) {
7622                 var s = siblings[i];
7623                 if(s.nodeType == 1){
7624                     Roo.get(s).removeClass(className);
7625                 }
7626             }
7627             this.addClass(className);
7628             return this;
7629         },
7630
7631         /**
7632          * Removes one or more CSS classes from the element.
7633          * @param {String/Array} className The CSS class to remove, or an array of classes
7634          * @return {Roo.Element} this
7635          */
7636         removeClass : function(className){
7637             if(!className || !this.dom.className){
7638                 return this;
7639             }
7640             if(className instanceof Array){
7641                 for(var i = 0, len = className.length; i < len; i++) {
7642                     this.removeClass(className[i]);
7643                 }
7644             }else{
7645                 if(this.hasClass(className)){
7646                     var re = this.classReCache[className];
7647                     if (!re) {
7648                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7649                        this.classReCache[className] = re;
7650                     }
7651                     this.dom.className =
7652                         this.dom.className.replace(re, " ");
7653                 }
7654             }
7655             return this;
7656         },
7657
7658         // private
7659         classReCache: {},
7660
7661         /**
7662          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7663          * @param {String} className The CSS class to toggle
7664          * @return {Roo.Element} this
7665          */
7666         toggleClass : function(className){
7667             if(this.hasClass(className)){
7668                 this.removeClass(className);
7669             }else{
7670                 this.addClass(className);
7671             }
7672             return this;
7673         },
7674
7675         /**
7676          * Checks if the specified CSS class exists on this element's DOM node.
7677          * @param {String} className The CSS class to check for
7678          * @return {Boolean} True if the class exists, else false
7679          */
7680         hasClass : function(className){
7681             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7682         },
7683
7684         /**
7685          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7686          * @param {String} oldClassName The CSS class to replace
7687          * @param {String} newClassName The replacement CSS class
7688          * @return {Roo.Element} this
7689          */
7690         replaceClass : function(oldClassName, newClassName){
7691             this.removeClass(oldClassName);
7692             this.addClass(newClassName);
7693             return this;
7694         },
7695
7696         /**
7697          * Returns an object with properties matching the styles requested.
7698          * For example, el.getStyles('color', 'font-size', 'width') might return
7699          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7700          * @param {String} style1 A style name
7701          * @param {String} style2 A style name
7702          * @param {String} etc.
7703          * @return {Object} The style object
7704          */
7705         getStyles : function(){
7706             var a = arguments, len = a.length, r = {};
7707             for(var i = 0; i < len; i++){
7708                 r[a[i]] = this.getStyle(a[i]);
7709             }
7710             return r;
7711         },
7712
7713         /**
7714          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7715          * @param {String} property The style property whose value is returned.
7716          * @return {String} The current value of the style property for this element.
7717          */
7718         getStyle : function(){
7719             return view && view.getComputedStyle ?
7720                 function(prop){
7721                     var el = this.dom, v, cs, camel;
7722                     if(prop == 'float'){
7723                         prop = "cssFloat";
7724                     }
7725                     if(el.style && (v = el.style[prop])){
7726                         return v;
7727                     }
7728                     if(cs = view.getComputedStyle(el, "")){
7729                         if(!(camel = propCache[prop])){
7730                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7731                         }
7732                         return cs[camel];
7733                     }
7734                     return null;
7735                 } :
7736                 function(prop){
7737                     var el = this.dom, v, cs, camel;
7738                     if(prop == 'opacity'){
7739                         if(typeof el.style.filter == 'string'){
7740                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7741                             if(m){
7742                                 var fv = parseFloat(m[1]);
7743                                 if(!isNaN(fv)){
7744                                     return fv ? fv / 100 : 0;
7745                                 }
7746                             }
7747                         }
7748                         return 1;
7749                     }else if(prop == 'float'){
7750                         prop = "styleFloat";
7751                     }
7752                     if(!(camel = propCache[prop])){
7753                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7754                     }
7755                     if(v = el.style[camel]){
7756                         return v;
7757                     }
7758                     if(cs = el.currentStyle){
7759                         return cs[camel];
7760                     }
7761                     return null;
7762                 };
7763         }(),
7764
7765         /**
7766          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7767          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7768          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7769          * @return {Roo.Element} this
7770          */
7771         setStyle : function(prop, value){
7772             if(typeof prop == "string"){
7773                 
7774                 if (prop == 'float') {
7775                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7776                     return this;
7777                 }
7778                 
7779                 var camel;
7780                 if(!(camel = propCache[prop])){
7781                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7782                 }
7783                 
7784                 if(camel == 'opacity') {
7785                     this.setOpacity(value);
7786                 }else{
7787                     this.dom.style[camel] = value;
7788                 }
7789             }else{
7790                 for(var style in prop){
7791                     if(typeof prop[style] != "function"){
7792                        this.setStyle(style, prop[style]);
7793                     }
7794                 }
7795             }
7796             return this;
7797         },
7798
7799         /**
7800          * More flexible version of {@link #setStyle} for setting style properties.
7801          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7802          * a function which returns such a specification.
7803          * @return {Roo.Element} this
7804          */
7805         applyStyles : function(style){
7806             Roo.DomHelper.applyStyles(this.dom, style);
7807             return this;
7808         },
7809
7810         /**
7811           * 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).
7812           * @return {Number} The X position of the element
7813           */
7814         getX : function(){
7815             return D.getX(this.dom);
7816         },
7817
7818         /**
7819           * 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).
7820           * @return {Number} The Y position of the element
7821           */
7822         getY : function(){
7823             return D.getY(this.dom);
7824         },
7825
7826         /**
7827           * 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).
7828           * @return {Array} The XY position of the element
7829           */
7830         getXY : function(){
7831             return D.getXY(this.dom);
7832         },
7833
7834         /**
7835          * 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).
7836          * @param {Number} The X position of the element
7837          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7838          * @return {Roo.Element} this
7839          */
7840         setX : function(x, animate){
7841             if(!animate || !A){
7842                 D.setX(this.dom, x);
7843             }else{
7844                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7845             }
7846             return this;
7847         },
7848
7849         /**
7850          * 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).
7851          * @param {Number} The Y position of the element
7852          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7853          * @return {Roo.Element} this
7854          */
7855         setY : function(y, animate){
7856             if(!animate || !A){
7857                 D.setY(this.dom, y);
7858             }else{
7859                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7860             }
7861             return this;
7862         },
7863
7864         /**
7865          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7866          * @param {String} left The left CSS property value
7867          * @return {Roo.Element} this
7868          */
7869         setLeft : function(left){
7870             this.setStyle("left", this.addUnits(left));
7871             return this;
7872         },
7873
7874         /**
7875          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7876          * @param {String} top The top CSS property value
7877          * @return {Roo.Element} this
7878          */
7879         setTop : function(top){
7880             this.setStyle("top", this.addUnits(top));
7881             return this;
7882         },
7883
7884         /**
7885          * Sets the element's CSS right style.
7886          * @param {String} right The right CSS property value
7887          * @return {Roo.Element} this
7888          */
7889         setRight : function(right){
7890             this.setStyle("right", this.addUnits(right));
7891             return this;
7892         },
7893
7894         /**
7895          * Sets the element's CSS bottom style.
7896          * @param {String} bottom The bottom CSS property value
7897          * @return {Roo.Element} this
7898          */
7899         setBottom : function(bottom){
7900             this.setStyle("bottom", this.addUnits(bottom));
7901             return this;
7902         },
7903
7904         /**
7905          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7906          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7907          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7908          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7909          * @return {Roo.Element} this
7910          */
7911         setXY : function(pos, animate){
7912             if(!animate || !A){
7913                 D.setXY(this.dom, pos);
7914             }else{
7915                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7916             }
7917             return this;
7918         },
7919
7920         /**
7921          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7922          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7923          * @param {Number} x X value for new position (coordinates are page-based)
7924          * @param {Number} y Y value for new position (coordinates are page-based)
7925          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7926          * @return {Roo.Element} this
7927          */
7928         setLocation : function(x, y, animate){
7929             this.setXY([x, y], this.preanim(arguments, 2));
7930             return this;
7931         },
7932
7933         /**
7934          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7935          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7936          * @param {Number} x X value for new position (coordinates are page-based)
7937          * @param {Number} y Y value for new position (coordinates are page-based)
7938          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7939          * @return {Roo.Element} this
7940          */
7941         moveTo : function(x, y, animate){
7942             this.setXY([x, y], this.preanim(arguments, 2));
7943             return this;
7944         },
7945
7946         /**
7947          * Returns the region of the given element.
7948          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7949          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7950          */
7951         getRegion : function(){
7952             return D.getRegion(this.dom);
7953         },
7954
7955         /**
7956          * Returns the offset height of the element
7957          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7958          * @return {Number} The element's height
7959          */
7960         getHeight : function(contentHeight){
7961             var h = this.dom.offsetHeight || 0;
7962             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7963         },
7964
7965         /**
7966          * Returns the offset width of the element
7967          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7968          * @return {Number} The element's width
7969          */
7970         getWidth : function(contentWidth){
7971             var w = this.dom.offsetWidth || 0;
7972             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7973         },
7974
7975         /**
7976          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7977          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7978          * if a height has not been set using CSS.
7979          * @return {Number}
7980          */
7981         getComputedHeight : function(){
7982             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7983             if(!h){
7984                 h = parseInt(this.getStyle('height'), 10) || 0;
7985                 if(!this.isBorderBox()){
7986                     h += this.getFrameWidth('tb');
7987                 }
7988             }
7989             return h;
7990         },
7991
7992         /**
7993          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7994          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7995          * if a width has not been set using CSS.
7996          * @return {Number}
7997          */
7998         getComputedWidth : function(){
7999             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8000             if(!w){
8001                 w = parseInt(this.getStyle('width'), 10) || 0;
8002                 if(!this.isBorderBox()){
8003                     w += this.getFrameWidth('lr');
8004                 }
8005             }
8006             return w;
8007         },
8008
8009         /**
8010          * Returns the size of the element.
8011          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8012          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8013          */
8014         getSize : function(contentSize){
8015             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8016         },
8017
8018         /**
8019          * Returns the width and height of the viewport.
8020          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8021          */
8022         getViewSize : function(){
8023             var d = this.dom, doc = document, aw = 0, ah = 0;
8024             if(d == doc || d == doc.body){
8025                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8026             }else{
8027                 return {
8028                     width : d.clientWidth,
8029                     height: d.clientHeight
8030                 };
8031             }
8032         },
8033
8034         /**
8035          * Returns the value of the "value" attribute
8036          * @param {Boolean} asNumber true to parse the value as a number
8037          * @return {String/Number}
8038          */
8039         getValue : function(asNumber){
8040             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8041         },
8042
8043         // private
8044         adjustWidth : function(width){
8045             if(typeof width == "number"){
8046                 if(this.autoBoxAdjust && !this.isBorderBox()){
8047                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8048                 }
8049                 if(width < 0){
8050                     width = 0;
8051                 }
8052             }
8053             return width;
8054         },
8055
8056         // private
8057         adjustHeight : function(height){
8058             if(typeof height == "number"){
8059                if(this.autoBoxAdjust && !this.isBorderBox()){
8060                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8061                }
8062                if(height < 0){
8063                    height = 0;
8064                }
8065             }
8066             return height;
8067         },
8068
8069         /**
8070          * Set the width of the element
8071          * @param {Number} width The new width
8072          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8073          * @return {Roo.Element} this
8074          */
8075         setWidth : function(width, animate){
8076             width = this.adjustWidth(width);
8077             if(!animate || !A){
8078                 this.dom.style.width = this.addUnits(width);
8079             }else{
8080                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8081             }
8082             return this;
8083         },
8084
8085         /**
8086          * Set the height of the element
8087          * @param {Number} height The new height
8088          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8089          * @return {Roo.Element} this
8090          */
8091          setHeight : function(height, animate){
8092             height = this.adjustHeight(height);
8093             if(!animate || !A){
8094                 this.dom.style.height = this.addUnits(height);
8095             }else{
8096                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8097             }
8098             return this;
8099         },
8100
8101         /**
8102          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8103          * @param {Number} width The new width
8104          * @param {Number} height The new height
8105          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8106          * @return {Roo.Element} this
8107          */
8108          setSize : function(width, height, animate){
8109             if(typeof width == "object"){ // in case of object from getSize()
8110                 height = width.height; width = width.width;
8111             }
8112             width = this.adjustWidth(width); height = this.adjustHeight(height);
8113             if(!animate || !A){
8114                 this.dom.style.width = this.addUnits(width);
8115                 this.dom.style.height = this.addUnits(height);
8116             }else{
8117                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8118             }
8119             return this;
8120         },
8121
8122         /**
8123          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8124          * @param {Number} x X value for new position (coordinates are page-based)
8125          * @param {Number} y Y value for new position (coordinates are page-based)
8126          * @param {Number} width The new width
8127          * @param {Number} height The new height
8128          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8129          * @return {Roo.Element} this
8130          */
8131         setBounds : function(x, y, width, height, animate){
8132             if(!animate || !A){
8133                 this.setSize(width, height);
8134                 this.setLocation(x, y);
8135             }else{
8136                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8137                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8138                               this.preanim(arguments, 4), 'motion');
8139             }
8140             return this;
8141         },
8142
8143         /**
8144          * 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.
8145          * @param {Roo.lib.Region} region The region to fill
8146          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8147          * @return {Roo.Element} this
8148          */
8149         setRegion : function(region, animate){
8150             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8151             return this;
8152         },
8153
8154         /**
8155          * Appends an event handler
8156          *
8157          * @param {String}   eventName     The type of event to append
8158          * @param {Function} fn        The method the event invokes
8159          * @param {Object} scope       (optional) The scope (this object) of the fn
8160          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8161          */
8162         addListener : function(eventName, fn, scope, options){
8163             if (this.dom) {
8164                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8165             }
8166         },
8167
8168         /**
8169          * Removes an event handler from this element
8170          * @param {String} eventName the type of event to remove
8171          * @param {Function} fn the method the event invokes
8172          * @return {Roo.Element} this
8173          */
8174         removeListener : function(eventName, fn){
8175             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8176             return this;
8177         },
8178
8179         /**
8180          * Removes all previous added listeners from this element
8181          * @return {Roo.Element} this
8182          */
8183         removeAllListeners : function(){
8184             E.purgeElement(this.dom);
8185             return this;
8186         },
8187
8188         relayEvent : function(eventName, observable){
8189             this.on(eventName, function(e){
8190                 observable.fireEvent(eventName, e);
8191             });
8192         },
8193
8194         /**
8195          * Set the opacity of the element
8196          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8197          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8198          * @return {Roo.Element} this
8199          */
8200          setOpacity : function(opacity, animate){
8201             if(!animate || !A){
8202                 var s = this.dom.style;
8203                 if(Roo.isIE){
8204                     s.zoom = 1;
8205                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8206                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8207                 }else{
8208                     s.opacity = opacity;
8209                 }
8210             }else{
8211                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8212             }
8213             return this;
8214         },
8215
8216         /**
8217          * Gets the left X coordinate
8218          * @param {Boolean} local True to get the local css position instead of page coordinate
8219          * @return {Number}
8220          */
8221         getLeft : function(local){
8222             if(!local){
8223                 return this.getX();
8224             }else{
8225                 return parseInt(this.getStyle("left"), 10) || 0;
8226             }
8227         },
8228
8229         /**
8230          * Gets the right X coordinate of the element (element X position + element width)
8231          * @param {Boolean} local True to get the local css position instead of page coordinate
8232          * @return {Number}
8233          */
8234         getRight : function(local){
8235             if(!local){
8236                 return this.getX() + this.getWidth();
8237             }else{
8238                 return (this.getLeft(true) + this.getWidth()) || 0;
8239             }
8240         },
8241
8242         /**
8243          * Gets the top Y coordinate
8244          * @param {Boolean} local True to get the local css position instead of page coordinate
8245          * @return {Number}
8246          */
8247         getTop : function(local) {
8248             if(!local){
8249                 return this.getY();
8250             }else{
8251                 return parseInt(this.getStyle("top"), 10) || 0;
8252             }
8253         },
8254
8255         /**
8256          * Gets the bottom Y coordinate of the element (element Y position + element height)
8257          * @param {Boolean} local True to get the local css position instead of page coordinate
8258          * @return {Number}
8259          */
8260         getBottom : function(local){
8261             if(!local){
8262                 return this.getY() + this.getHeight();
8263             }else{
8264                 return (this.getTop(true) + this.getHeight()) || 0;
8265             }
8266         },
8267
8268         /**
8269         * Initializes positioning on this element. If a desired position is not passed, it will make the
8270         * the element positioned relative IF it is not already positioned.
8271         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8272         * @param {Number} zIndex (optional) The zIndex to apply
8273         * @param {Number} x (optional) Set the page X position
8274         * @param {Number} y (optional) Set the page Y position
8275         */
8276         position : function(pos, zIndex, x, y){
8277             if(!pos){
8278                if(this.getStyle('position') == 'static'){
8279                    this.setStyle('position', 'relative');
8280                }
8281             }else{
8282                 this.setStyle("position", pos);
8283             }
8284             if(zIndex){
8285                 this.setStyle("z-index", zIndex);
8286             }
8287             if(x !== undefined && y !== undefined){
8288                 this.setXY([x, y]);
8289             }else if(x !== undefined){
8290                 this.setX(x);
8291             }else if(y !== undefined){
8292                 this.setY(y);
8293             }
8294         },
8295
8296         /**
8297         * Clear positioning back to the default when the document was loaded
8298         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8299         * @return {Roo.Element} this
8300          */
8301         clearPositioning : function(value){
8302             value = value ||'';
8303             this.setStyle({
8304                 "left": value,
8305                 "right": value,
8306                 "top": value,
8307                 "bottom": value,
8308                 "z-index": "",
8309                 "position" : "static"
8310             });
8311             return this;
8312         },
8313
8314         /**
8315         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8316         * snapshot before performing an update and then restoring the element.
8317         * @return {Object}
8318         */
8319         getPositioning : function(){
8320             var l = this.getStyle("left");
8321             var t = this.getStyle("top");
8322             return {
8323                 "position" : this.getStyle("position"),
8324                 "left" : l,
8325                 "right" : l ? "" : this.getStyle("right"),
8326                 "top" : t,
8327                 "bottom" : t ? "" : this.getStyle("bottom"),
8328                 "z-index" : this.getStyle("z-index")
8329             };
8330         },
8331
8332         /**
8333          * Gets the width of the border(s) for the specified side(s)
8334          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8335          * passing lr would get the border (l)eft width + the border (r)ight width.
8336          * @return {Number} The width of the sides passed added together
8337          */
8338         getBorderWidth : function(side){
8339             return this.addStyles(side, El.borders);
8340         },
8341
8342         /**
8343          * Gets the width of the padding(s) for the specified side(s)
8344          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8345          * passing lr would get the padding (l)eft + the padding (r)ight.
8346          * @return {Number} The padding of the sides passed added together
8347          */
8348         getPadding : function(side){
8349             return this.addStyles(side, El.paddings);
8350         },
8351
8352         /**
8353         * Set positioning with an object returned by getPositioning().
8354         * @param {Object} posCfg
8355         * @return {Roo.Element} this
8356          */
8357         setPositioning : function(pc){
8358             this.applyStyles(pc);
8359             if(pc.right == "auto"){
8360                 this.dom.style.right = "";
8361             }
8362             if(pc.bottom == "auto"){
8363                 this.dom.style.bottom = "";
8364             }
8365             return this;
8366         },
8367
8368         // private
8369         fixDisplay : function(){
8370             if(this.getStyle("display") == "none"){
8371                 this.setStyle("visibility", "hidden");
8372                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8373                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8374                     this.setStyle("display", "block");
8375                 }
8376             }
8377         },
8378
8379         /**
8380          * Quick set left and top adding default units
8381          * @param {String} left The left CSS property value
8382          * @param {String} top The top CSS property value
8383          * @return {Roo.Element} this
8384          */
8385          setLeftTop : function(left, top){
8386             this.dom.style.left = this.addUnits(left);
8387             this.dom.style.top = this.addUnits(top);
8388             return this;
8389         },
8390
8391         /**
8392          * Move this element relative to its current position.
8393          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8394          * @param {Number} distance How far to move the element in pixels
8395          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8396          * @return {Roo.Element} this
8397          */
8398          move : function(direction, distance, animate){
8399             var xy = this.getXY();
8400             direction = direction.toLowerCase();
8401             switch(direction){
8402                 case "l":
8403                 case "left":
8404                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8405                     break;
8406                case "r":
8407                case "right":
8408                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8409                     break;
8410                case "t":
8411                case "top":
8412                case "up":
8413                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8414                     break;
8415                case "b":
8416                case "bottom":
8417                case "down":
8418                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8419                     break;
8420             }
8421             return this;
8422         },
8423
8424         /**
8425          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8426          * @return {Roo.Element} this
8427          */
8428         clip : function(){
8429             if(!this.isClipped){
8430                this.isClipped = true;
8431                this.originalClip = {
8432                    "o": this.getStyle("overflow"),
8433                    "x": this.getStyle("overflow-x"),
8434                    "y": this.getStyle("overflow-y")
8435                };
8436                this.setStyle("overflow", "hidden");
8437                this.setStyle("overflow-x", "hidden");
8438                this.setStyle("overflow-y", "hidden");
8439             }
8440             return this;
8441         },
8442
8443         /**
8444          *  Return clipping (overflow) to original clipping before clip() was called
8445          * @return {Roo.Element} this
8446          */
8447         unclip : function(){
8448             if(this.isClipped){
8449                 this.isClipped = false;
8450                 var o = this.originalClip;
8451                 if(o.o){this.setStyle("overflow", o.o);}
8452                 if(o.x){this.setStyle("overflow-x", o.x);}
8453                 if(o.y){this.setStyle("overflow-y", o.y);}
8454             }
8455             return this;
8456         },
8457
8458
8459         /**
8460          * Gets the x,y coordinates specified by the anchor position on the element.
8461          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8462          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8463          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8464          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8465          * @return {Array} [x, y] An array containing the element's x and y coordinates
8466          */
8467         getAnchorXY : function(anchor, local, s){
8468             //Passing a different size is useful for pre-calculating anchors,
8469             //especially for anchored animations that change the el size.
8470
8471             var w, h, vp = false;
8472             if(!s){
8473                 var d = this.dom;
8474                 if(d == document.body || d == document){
8475                     vp = true;
8476                     w = D.getViewWidth(); h = D.getViewHeight();
8477                 }else{
8478                     w = this.getWidth(); h = this.getHeight();
8479                 }
8480             }else{
8481                 w = s.width;  h = s.height;
8482             }
8483             var x = 0, y = 0, r = Math.round;
8484             switch((anchor || "tl").toLowerCase()){
8485                 case "c":
8486                     x = r(w*.5);
8487                     y = r(h*.5);
8488                 break;
8489                 case "t":
8490                     x = r(w*.5);
8491                     y = 0;
8492                 break;
8493                 case "l":
8494                     x = 0;
8495                     y = r(h*.5);
8496                 break;
8497                 case "r":
8498                     x = w;
8499                     y = r(h*.5);
8500                 break;
8501                 case "b":
8502                     x = r(w*.5);
8503                     y = h;
8504                 break;
8505                 case "tl":
8506                     x = 0;
8507                     y = 0;
8508                 break;
8509                 case "bl":
8510                     x = 0;
8511                     y = h;
8512                 break;
8513                 case "br":
8514                     x = w;
8515                     y = h;
8516                 break;
8517                 case "tr":
8518                     x = w;
8519                     y = 0;
8520                 break;
8521             }
8522             if(local === true){
8523                 return [x, y];
8524             }
8525             if(vp){
8526                 var sc = this.getScroll();
8527                 return [x + sc.left, y + sc.top];
8528             }
8529             //Add the element's offset xy
8530             var o = this.getXY();
8531             return [x+o[0], y+o[1]];
8532         },
8533
8534         /**
8535          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8536          * supported position values.
8537          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8538          * @param {String} position The position to align to.
8539          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8540          * @return {Array} [x, y]
8541          */
8542         getAlignToXY : function(el, p, o){
8543             el = Roo.get(el);
8544             var d = this.dom;
8545             if(!el.dom){
8546                 throw "Element.alignTo with an element that doesn't exist";
8547             }
8548             var c = false; //constrain to viewport
8549             var p1 = "", p2 = "";
8550             o = o || [0,0];
8551
8552             if(!p){
8553                 p = "tl-bl";
8554             }else if(p == "?"){
8555                 p = "tl-bl?";
8556             }else if(p.indexOf("-") == -1){
8557                 p = "tl-" + p;
8558             }
8559             p = p.toLowerCase();
8560             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8561             if(!m){
8562                throw "Element.alignTo with an invalid alignment " + p;
8563             }
8564             p1 = m[1]; p2 = m[2]; c = !!m[3];
8565
8566             //Subtract the aligned el's internal xy from the target's offset xy
8567             //plus custom offset to get the aligned el's new offset xy
8568             var a1 = this.getAnchorXY(p1, true);
8569             var a2 = el.getAnchorXY(p2, false);
8570             var x = a2[0] - a1[0] + o[0];
8571             var y = a2[1] - a1[1] + o[1];
8572             if(c){
8573                 //constrain the aligned el to viewport if necessary
8574                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8575                 // 5px of margin for ie
8576                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8577
8578                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8579                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8580                 //otherwise swap the aligned el to the opposite border of the target.
8581                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8582                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8583                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8584                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8585
8586                var doc = document;
8587                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8588                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8589
8590                if((x+w) > dw + scrollX){
8591                     x = swapX ? r.left-w : dw+scrollX-w;
8592                 }
8593                if(x < scrollX){
8594                    x = swapX ? r.right : scrollX;
8595                }
8596                if((y+h) > dh + scrollY){
8597                     y = swapY ? r.top-h : dh+scrollY-h;
8598                 }
8599                if (y < scrollY){
8600                    y = swapY ? r.bottom : scrollY;
8601                }
8602             }
8603             return [x,y];
8604         },
8605
8606         // private
8607         getConstrainToXY : function(){
8608             var os = {top:0, left:0, bottom:0, right: 0};
8609
8610             return function(el, local, offsets, proposedXY){
8611                 el = Roo.get(el);
8612                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8613
8614                 var vw, vh, vx = 0, vy = 0;
8615                 if(el.dom == document.body || el.dom == document){
8616                     vw = Roo.lib.Dom.getViewWidth();
8617                     vh = Roo.lib.Dom.getViewHeight();
8618                 }else{
8619                     vw = el.dom.clientWidth;
8620                     vh = el.dom.clientHeight;
8621                     if(!local){
8622                         var vxy = el.getXY();
8623                         vx = vxy[0];
8624                         vy = vxy[1];
8625                     }
8626                 }
8627
8628                 var s = el.getScroll();
8629
8630                 vx += offsets.left + s.left;
8631                 vy += offsets.top + s.top;
8632
8633                 vw -= offsets.right;
8634                 vh -= offsets.bottom;
8635
8636                 var vr = vx+vw;
8637                 var vb = vy+vh;
8638
8639                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8640                 var x = xy[0], y = xy[1];
8641                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8642
8643                 // only move it if it needs it
8644                 var moved = false;
8645
8646                 // first validate right/bottom
8647                 if((x + w) > vr){
8648                     x = vr - w;
8649                     moved = true;
8650                 }
8651                 if((y + h) > vb){
8652                     y = vb - h;
8653                     moved = true;
8654                 }
8655                 // then make sure top/left isn't negative
8656                 if(x < vx){
8657                     x = vx;
8658                     moved = true;
8659                 }
8660                 if(y < vy){
8661                     y = vy;
8662                     moved = true;
8663                 }
8664                 return moved ? [x, y] : false;
8665             };
8666         }(),
8667
8668         // private
8669         adjustForConstraints : function(xy, parent, offsets){
8670             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8671         },
8672
8673         /**
8674          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8675          * document it aligns it to the viewport.
8676          * The position parameter is optional, and can be specified in any one of the following formats:
8677          * <ul>
8678          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8679          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8680          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8681          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8682          *   <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
8683          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8684          * </ul>
8685          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8686          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8687          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8688          * that specified in order to enforce the viewport constraints.
8689          * Following are all of the supported anchor positions:
8690     <pre>
8691     Value  Description
8692     -----  -----------------------------
8693     tl     The top left corner (default)
8694     t      The center of the top edge
8695     tr     The top right corner
8696     l      The center of the left edge
8697     c      In the center of the element
8698     r      The center of the right edge
8699     bl     The bottom left corner
8700     b      The center of the bottom edge
8701     br     The bottom right corner
8702     </pre>
8703     Example Usage:
8704     <pre><code>
8705     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8706     el.alignTo("other-el");
8707
8708     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8709     el.alignTo("other-el", "tr?");
8710
8711     // align the bottom right corner of el with the center left edge of other-el
8712     el.alignTo("other-el", "br-l?");
8713
8714     // align the center of el with the bottom left corner of other-el and
8715     // adjust the x position by -6 pixels (and the y position by 0)
8716     el.alignTo("other-el", "c-bl", [-6, 0]);
8717     </code></pre>
8718          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8719          * @param {String} position The position to align to.
8720          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8721          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8722          * @return {Roo.Element} this
8723          */
8724         alignTo : function(element, position, offsets, animate){
8725             var xy = this.getAlignToXY(element, position, offsets);
8726             this.setXY(xy, this.preanim(arguments, 3));
8727             return this;
8728         },
8729
8730         /**
8731          * Anchors an element to another element and realigns it when the window is resized.
8732          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8733          * @param {String} position The position to align to.
8734          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8735          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8736          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8737          * is a number, it is used as the buffer delay (defaults to 50ms).
8738          * @param {Function} callback The function to call after the animation finishes
8739          * @return {Roo.Element} this
8740          */
8741         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8742             var action = function(){
8743                 this.alignTo(el, alignment, offsets, animate);
8744                 Roo.callback(callback, this);
8745             };
8746             Roo.EventManager.onWindowResize(action, this);
8747             var tm = typeof monitorScroll;
8748             if(tm != 'undefined'){
8749                 Roo.EventManager.on(window, 'scroll', action, this,
8750                     {buffer: tm == 'number' ? monitorScroll : 50});
8751             }
8752             action.call(this); // align immediately
8753             return this;
8754         },
8755         /**
8756          * Clears any opacity settings from this element. Required in some cases for IE.
8757          * @return {Roo.Element} this
8758          */
8759         clearOpacity : function(){
8760             if (window.ActiveXObject) {
8761                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8762                     this.dom.style.filter = "";
8763                 }
8764             } else {
8765                 this.dom.style.opacity = "";
8766                 this.dom.style["-moz-opacity"] = "";
8767                 this.dom.style["-khtml-opacity"] = "";
8768             }
8769             return this;
8770         },
8771
8772         /**
8773          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8774          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8775          * @return {Roo.Element} this
8776          */
8777         hide : function(animate){
8778             this.setVisible(false, this.preanim(arguments, 0));
8779             return this;
8780         },
8781
8782         /**
8783         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8784         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8785          * @return {Roo.Element} this
8786          */
8787         show : function(animate){
8788             this.setVisible(true, this.preanim(arguments, 0));
8789             return this;
8790         },
8791
8792         /**
8793          * @private Test if size has a unit, otherwise appends the default
8794          */
8795         addUnits : function(size){
8796             return Roo.Element.addUnits(size, this.defaultUnit);
8797         },
8798
8799         /**
8800          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8801          * @return {Roo.Element} this
8802          */
8803         beginMeasure : function(){
8804             var el = this.dom;
8805             if(el.offsetWidth || el.offsetHeight){
8806                 return this; // offsets work already
8807             }
8808             var changed = [];
8809             var p = this.dom, b = document.body; // start with this element
8810             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8811                 var pe = Roo.get(p);
8812                 if(pe.getStyle('display') == 'none'){
8813                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8814                     p.style.visibility = "hidden";
8815                     p.style.display = "block";
8816                 }
8817                 p = p.parentNode;
8818             }
8819             this._measureChanged = changed;
8820             return this;
8821
8822         },
8823
8824         /**
8825          * Restores displays to before beginMeasure was called
8826          * @return {Roo.Element} this
8827          */
8828         endMeasure : function(){
8829             var changed = this._measureChanged;
8830             if(changed){
8831                 for(var i = 0, len = changed.length; i < len; i++) {
8832                     var r = changed[i];
8833                     r.el.style.visibility = r.visibility;
8834                     r.el.style.display = "none";
8835                 }
8836                 this._measureChanged = null;
8837             }
8838             return this;
8839         },
8840
8841         /**
8842         * Update the innerHTML of this element, optionally searching for and processing scripts
8843         * @param {String} html The new HTML
8844         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8845         * @param {Function} callback For async script loading you can be noticed when the update completes
8846         * @return {Roo.Element} this
8847          */
8848         update : function(html, loadScripts, callback){
8849             if(typeof html == "undefined"){
8850                 html = "";
8851             }
8852             if(loadScripts !== true){
8853                 this.dom.innerHTML = html;
8854                 if(typeof callback == "function"){
8855                     callback();
8856                 }
8857                 return this;
8858             }
8859             var id = Roo.id();
8860             var dom = this.dom;
8861
8862             html += '<span id="' + id + '"></span>';
8863
8864             E.onAvailable(id, function(){
8865                 var hd = document.getElementsByTagName("head")[0];
8866                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8867                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8868                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8869
8870                 var match;
8871                 while(match = re.exec(html)){
8872                     var attrs = match[1];
8873                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8874                     if(srcMatch && srcMatch[2]){
8875                        var s = document.createElement("script");
8876                        s.src = srcMatch[2];
8877                        var typeMatch = attrs.match(typeRe);
8878                        if(typeMatch && typeMatch[2]){
8879                            s.type = typeMatch[2];
8880                        }
8881                        hd.appendChild(s);
8882                     }else if(match[2] && match[2].length > 0){
8883                         if(window.execScript) {
8884                            window.execScript(match[2]);
8885                         } else {
8886                             /**
8887                              * eval:var:id
8888                              * eval:var:dom
8889                              * eval:var:html
8890                              * 
8891                              */
8892                            window.eval(match[2]);
8893                         }
8894                     }
8895                 }
8896                 var el = document.getElementById(id);
8897                 if(el){el.parentNode.removeChild(el);}
8898                 if(typeof callback == "function"){
8899                     callback();
8900                 }
8901             });
8902             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8903             return this;
8904         },
8905
8906         /**
8907          * Direct access to the UpdateManager update() method (takes the same parameters).
8908          * @param {String/Function} url The url for this request or a function to call to get the url
8909          * @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}
8910          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8911          * @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.
8912          * @return {Roo.Element} this
8913          */
8914         load : function(){
8915             var um = this.getUpdateManager();
8916             um.update.apply(um, arguments);
8917             return this;
8918         },
8919
8920         /**
8921         * Gets this element's UpdateManager
8922         * @return {Roo.UpdateManager} The UpdateManager
8923         */
8924         getUpdateManager : function(){
8925             if(!this.updateManager){
8926                 this.updateManager = new Roo.UpdateManager(this);
8927             }
8928             return this.updateManager;
8929         },
8930
8931         /**
8932          * Disables text selection for this element (normalized across browsers)
8933          * @return {Roo.Element} this
8934          */
8935         unselectable : function(){
8936             this.dom.unselectable = "on";
8937             this.swallowEvent("selectstart", true);
8938             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8939             this.addClass("x-unselectable");
8940             return this;
8941         },
8942
8943         /**
8944         * Calculates the x, y to center this element on the screen
8945         * @return {Array} The x, y values [x, y]
8946         */
8947         getCenterXY : function(){
8948             return this.getAlignToXY(document, 'c-c');
8949         },
8950
8951         /**
8952         * Centers the Element in either the viewport, or another Element.
8953         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8954         */
8955         center : function(centerIn){
8956             this.alignTo(centerIn || document, 'c-c');
8957             return this;
8958         },
8959
8960         /**
8961          * Tests various css rules/browsers to determine if this element uses a border box
8962          * @return {Boolean}
8963          */
8964         isBorderBox : function(){
8965             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8966         },
8967
8968         /**
8969          * Return a box {x, y, width, height} that can be used to set another elements
8970          * size/location to match this element.
8971          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8972          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8973          * @return {Object} box An object in the format {x, y, width, height}
8974          */
8975         getBox : function(contentBox, local){
8976             var xy;
8977             if(!local){
8978                 xy = this.getXY();
8979             }else{
8980                 var left = parseInt(this.getStyle("left"), 10) || 0;
8981                 var top = parseInt(this.getStyle("top"), 10) || 0;
8982                 xy = [left, top];
8983             }
8984             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8985             if(!contentBox){
8986                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8987             }else{
8988                 var l = this.getBorderWidth("l")+this.getPadding("l");
8989                 var r = this.getBorderWidth("r")+this.getPadding("r");
8990                 var t = this.getBorderWidth("t")+this.getPadding("t");
8991                 var b = this.getBorderWidth("b")+this.getPadding("b");
8992                 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)};
8993             }
8994             bx.right = bx.x + bx.width;
8995             bx.bottom = bx.y + bx.height;
8996             return bx;
8997         },
8998
8999         /**
9000          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9001          for more information about the sides.
9002          * @param {String} sides
9003          * @return {Number}
9004          */
9005         getFrameWidth : function(sides, onlyContentBox){
9006             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9007         },
9008
9009         /**
9010          * 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.
9011          * @param {Object} box The box to fill {x, y, width, height}
9012          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9013          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9014          * @return {Roo.Element} this
9015          */
9016         setBox : function(box, adjust, animate){
9017             var w = box.width, h = box.height;
9018             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9019                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9020                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9021             }
9022             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9023             return this;
9024         },
9025
9026         /**
9027          * Forces the browser to repaint this element
9028          * @return {Roo.Element} this
9029          */
9030          repaint : function(){
9031             var dom = this.dom;
9032             this.addClass("x-repaint");
9033             setTimeout(function(){
9034                 Roo.get(dom).removeClass("x-repaint");
9035             }, 1);
9036             return this;
9037         },
9038
9039         /**
9040          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9041          * then it returns the calculated width of the sides (see getPadding)
9042          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9043          * @return {Object/Number}
9044          */
9045         getMargins : function(side){
9046             if(!side){
9047                 return {
9048                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9049                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9050                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9051                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9052                 };
9053             }else{
9054                 return this.addStyles(side, El.margins);
9055              }
9056         },
9057
9058         // private
9059         addStyles : function(sides, styles){
9060             var val = 0, v, w;
9061             for(var i = 0, len = sides.length; i < len; i++){
9062                 v = this.getStyle(styles[sides.charAt(i)]);
9063                 if(v){
9064                      w = parseInt(v, 10);
9065                      if(w){ val += w; }
9066                 }
9067             }
9068             return val;
9069         },
9070
9071         /**
9072          * Creates a proxy element of this element
9073          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9074          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9075          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9076          * @return {Roo.Element} The new proxy element
9077          */
9078         createProxy : function(config, renderTo, matchBox){
9079             if(renderTo){
9080                 renderTo = Roo.getDom(renderTo);
9081             }else{
9082                 renderTo = document.body;
9083             }
9084             config = typeof config == "object" ?
9085                 config : {tag : "div", cls: config};
9086             var proxy = Roo.DomHelper.append(renderTo, config, true);
9087             if(matchBox){
9088                proxy.setBox(this.getBox());
9089             }
9090             return proxy;
9091         },
9092
9093         /**
9094          * Puts a mask over this element to disable user interaction. Requires core.css.
9095          * This method can only be applied to elements which accept child nodes.
9096          * @param {String} msg (optional) A message to display in the mask
9097          * @param {String} msgCls (optional) A css class to apply to the msg element
9098          * @return {Element} The mask  element
9099          */
9100         mask : function(msg, msgCls)
9101         {
9102             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9103                 this.setStyle("position", "relative");
9104             }
9105             if(!this._mask){
9106                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9107             }
9108             this.addClass("x-masked");
9109             this._mask.setDisplayed(true);
9110             
9111             // we wander
9112             var z = 0;
9113             var dom = this.dom;
9114             while (dom && dom.style) {
9115                 if (!isNaN(parseInt(dom.style.zIndex))) {
9116                     z = Math.max(z, parseInt(dom.style.zIndex));
9117                 }
9118                 dom = dom.parentNode;
9119             }
9120             // if we are masking the body - then it hides everything..
9121             if (this.dom == document.body) {
9122                 z = 1000000;
9123                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9124                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9125             }
9126            
9127             if(typeof msg == 'string'){
9128                 if(!this._maskMsg){
9129                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9130                 }
9131                 var mm = this._maskMsg;
9132                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9133                 if (mm.dom.firstChild) { // weird IE issue?
9134                     mm.dom.firstChild.innerHTML = msg;
9135                 }
9136                 mm.setDisplayed(true);
9137                 mm.center(this);
9138                 mm.setStyle('z-index', z + 102);
9139             }
9140             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9141                 this._mask.setHeight(this.getHeight());
9142             }
9143             this._mask.setStyle('z-index', z + 100);
9144             
9145             return this._mask;
9146         },
9147
9148         /**
9149          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9150          * it is cached for reuse.
9151          */
9152         unmask : function(removeEl){
9153             if(this._mask){
9154                 if(removeEl === true){
9155                     this._mask.remove();
9156                     delete this._mask;
9157                     if(this._maskMsg){
9158                         this._maskMsg.remove();
9159                         delete this._maskMsg;
9160                     }
9161                 }else{
9162                     this._mask.setDisplayed(false);
9163                     if(this._maskMsg){
9164                         this._maskMsg.setDisplayed(false);
9165                     }
9166                 }
9167             }
9168             this.removeClass("x-masked");
9169         },
9170
9171         /**
9172          * Returns true if this element is masked
9173          * @return {Boolean}
9174          */
9175         isMasked : function(){
9176             return this._mask && this._mask.isVisible();
9177         },
9178
9179         /**
9180          * Creates an iframe shim for this element to keep selects and other windowed objects from
9181          * showing through.
9182          * @return {Roo.Element} The new shim element
9183          */
9184         createShim : function(){
9185             var el = document.createElement('iframe');
9186             el.frameBorder = 'no';
9187             el.className = 'roo-shim';
9188             if(Roo.isIE && Roo.isSecure){
9189                 el.src = Roo.SSL_SECURE_URL;
9190             }
9191             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9192             shim.autoBoxAdjust = false;
9193             return shim;
9194         },
9195
9196         /**
9197          * Removes this element from the DOM and deletes it from the cache
9198          */
9199         remove : function(){
9200             if(this.dom.parentNode){
9201                 this.dom.parentNode.removeChild(this.dom);
9202             }
9203             delete El.cache[this.dom.id];
9204         },
9205
9206         /**
9207          * Sets up event handlers to add and remove a css class when the mouse is over this element
9208          * @param {String} className
9209          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9210          * mouseout events for children elements
9211          * @return {Roo.Element} this
9212          */
9213         addClassOnOver : function(className, preventFlicker){
9214             this.on("mouseover", function(){
9215                 Roo.fly(this, '_internal').addClass(className);
9216             }, this.dom);
9217             var removeFn = function(e){
9218                 if(preventFlicker !== true || !e.within(this, true)){
9219                     Roo.fly(this, '_internal').removeClass(className);
9220                 }
9221             };
9222             this.on("mouseout", removeFn, this.dom);
9223             return this;
9224         },
9225
9226         /**
9227          * Sets up event handlers to add and remove a css class when this element has the focus
9228          * @param {String} className
9229          * @return {Roo.Element} this
9230          */
9231         addClassOnFocus : function(className){
9232             this.on("focus", function(){
9233                 Roo.fly(this, '_internal').addClass(className);
9234             }, this.dom);
9235             this.on("blur", function(){
9236                 Roo.fly(this, '_internal').removeClass(className);
9237             }, this.dom);
9238             return this;
9239         },
9240         /**
9241          * 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)
9242          * @param {String} className
9243          * @return {Roo.Element} this
9244          */
9245         addClassOnClick : function(className){
9246             var dom = this.dom;
9247             this.on("mousedown", function(){
9248                 Roo.fly(dom, '_internal').addClass(className);
9249                 var d = Roo.get(document);
9250                 var fn = function(){
9251                     Roo.fly(dom, '_internal').removeClass(className);
9252                     d.removeListener("mouseup", fn);
9253                 };
9254                 d.on("mouseup", fn);
9255             });
9256             return this;
9257         },
9258
9259         /**
9260          * Stops the specified event from bubbling and optionally prevents the default action
9261          * @param {String} eventName
9262          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9263          * @return {Roo.Element} this
9264          */
9265         swallowEvent : function(eventName, preventDefault){
9266             var fn = function(e){
9267                 e.stopPropagation();
9268                 if(preventDefault){
9269                     e.preventDefault();
9270                 }
9271             };
9272             if(eventName instanceof Array){
9273                 for(var i = 0, len = eventName.length; i < len; i++){
9274                      this.on(eventName[i], fn);
9275                 }
9276                 return this;
9277             }
9278             this.on(eventName, fn);
9279             return this;
9280         },
9281
9282         /**
9283          * @private
9284          */
9285       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9286
9287         /**
9288          * Sizes this element to its parent element's dimensions performing
9289          * neccessary box adjustments.
9290          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9291          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9292          * @return {Roo.Element} this
9293          */
9294         fitToParent : function(monitorResize, targetParent) {
9295           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9296           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9297           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9298             return;
9299           }
9300           var p = Roo.get(targetParent || this.dom.parentNode);
9301           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9302           if (monitorResize === true) {
9303             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9304             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9305           }
9306           return this;
9307         },
9308
9309         /**
9310          * Gets the next sibling, skipping text nodes
9311          * @return {HTMLElement} The next sibling or null
9312          */
9313         getNextSibling : function(){
9314             var n = this.dom.nextSibling;
9315             while(n && n.nodeType != 1){
9316                 n = n.nextSibling;
9317             }
9318             return n;
9319         },
9320
9321         /**
9322          * Gets the previous sibling, skipping text nodes
9323          * @return {HTMLElement} The previous sibling or null
9324          */
9325         getPrevSibling : function(){
9326             var n = this.dom.previousSibling;
9327             while(n && n.nodeType != 1){
9328                 n = n.previousSibling;
9329             }
9330             return n;
9331         },
9332
9333
9334         /**
9335          * Appends the passed element(s) to this element
9336          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9337          * @return {Roo.Element} this
9338          */
9339         appendChild: function(el){
9340             el = Roo.get(el);
9341             el.appendTo(this);
9342             return this;
9343         },
9344
9345         /**
9346          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9347          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9348          * automatically generated with the specified attributes.
9349          * @param {HTMLElement} insertBefore (optional) a child element of this element
9350          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9351          * @return {Roo.Element} The new child element
9352          */
9353         createChild: function(config, insertBefore, returnDom){
9354             config = config || {tag:'div'};
9355             if(insertBefore){
9356                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9357             }
9358             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9359         },
9360
9361         /**
9362          * Appends this element to the passed element
9363          * @param {String/HTMLElement/Element} el The new parent element
9364          * @return {Roo.Element} this
9365          */
9366         appendTo: function(el){
9367             el = Roo.getDom(el);
9368             el.appendChild(this.dom);
9369             return this;
9370         },
9371
9372         /**
9373          * Inserts this element before the passed element in the DOM
9374          * @param {String/HTMLElement/Element} el The element to insert before
9375          * @return {Roo.Element} this
9376          */
9377         insertBefore: function(el){
9378             el = Roo.getDom(el);
9379             el.parentNode.insertBefore(this.dom, el);
9380             return this;
9381         },
9382
9383         /**
9384          * Inserts this element after the passed element in the DOM
9385          * @param {String/HTMLElement/Element} el The element to insert after
9386          * @return {Roo.Element} this
9387          */
9388         insertAfter: function(el){
9389             el = Roo.getDom(el);
9390             el.parentNode.insertBefore(this.dom, el.nextSibling);
9391             return this;
9392         },
9393
9394         /**
9395          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9396          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9397          * @return {Roo.Element} The new child
9398          */
9399         insertFirst: function(el, returnDom){
9400             el = el || {};
9401             if(typeof el == 'object' && !el.nodeType){ // dh config
9402                 return this.createChild(el, this.dom.firstChild, returnDom);
9403             }else{
9404                 el = Roo.getDom(el);
9405                 this.dom.insertBefore(el, this.dom.firstChild);
9406                 return !returnDom ? Roo.get(el) : el;
9407             }
9408         },
9409
9410         /**
9411          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9412          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9413          * @param {String} where (optional) 'before' or 'after' defaults to before
9414          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9415          * @return {Roo.Element} the inserted Element
9416          */
9417         insertSibling: function(el, where, returnDom){
9418             where = where ? where.toLowerCase() : 'before';
9419             el = el || {};
9420             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9421
9422             if(typeof el == 'object' && !el.nodeType){ // dh config
9423                 if(where == 'after' && !this.dom.nextSibling){
9424                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9425                 }else{
9426                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9427                 }
9428
9429             }else{
9430                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9431                             where == 'before' ? this.dom : this.dom.nextSibling);
9432                 if(!returnDom){
9433                     rt = Roo.get(rt);
9434                 }
9435             }
9436             return rt;
9437         },
9438
9439         /**
9440          * Creates and wraps this element with another element
9441          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9442          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9443          * @return {HTMLElement/Element} The newly created wrapper element
9444          */
9445         wrap: function(config, returnDom){
9446             if(!config){
9447                 config = {tag: "div"};
9448             }
9449             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9450             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9451             return newEl;
9452         },
9453
9454         /**
9455          * Replaces the passed element with this element
9456          * @param {String/HTMLElement/Element} el The element to replace
9457          * @return {Roo.Element} this
9458          */
9459         replace: function(el){
9460             el = Roo.get(el);
9461             this.insertBefore(el);
9462             el.remove();
9463             return this;
9464         },
9465
9466         /**
9467          * Inserts an html fragment into this element
9468          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9469          * @param {String} html The HTML fragment
9470          * @param {Boolean} returnEl True to return an Roo.Element
9471          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9472          */
9473         insertHtml : function(where, html, returnEl){
9474             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9475             return returnEl ? Roo.get(el) : el;
9476         },
9477
9478         /**
9479          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9480          * @param {Object} o The object with the attributes
9481          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9482          * @return {Roo.Element} this
9483          */
9484         set : function(o, useSet){
9485             var el = this.dom;
9486             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9487             for(var attr in o){
9488                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9489                 if(attr=="cls"){
9490                     el.className = o["cls"];
9491                 }else{
9492                     if(useSet) {
9493                         el.setAttribute(attr, o[attr]);
9494                     } else {
9495                         el[attr] = o[attr];
9496                     }
9497                 }
9498             }
9499             if(o.style){
9500                 Roo.DomHelper.applyStyles(el, o.style);
9501             }
9502             return this;
9503         },
9504
9505         /**
9506          * Convenience method for constructing a KeyMap
9507          * @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:
9508          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9509          * @param {Function} fn The function to call
9510          * @param {Object} scope (optional) The scope of the function
9511          * @return {Roo.KeyMap} The KeyMap created
9512          */
9513         addKeyListener : function(key, fn, scope){
9514             var config;
9515             if(typeof key != "object" || key instanceof Array){
9516                 config = {
9517                     key: key,
9518                     fn: fn,
9519                     scope: scope
9520                 };
9521             }else{
9522                 config = {
9523                     key : key.key,
9524                     shift : key.shift,
9525                     ctrl : key.ctrl,
9526                     alt : key.alt,
9527                     fn: fn,
9528                     scope: scope
9529                 };
9530             }
9531             return new Roo.KeyMap(this, config);
9532         },
9533
9534         /**
9535          * Creates a KeyMap for this element
9536          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9537          * @return {Roo.KeyMap} The KeyMap created
9538          */
9539         addKeyMap : function(config){
9540             return new Roo.KeyMap(this, config);
9541         },
9542
9543         /**
9544          * Returns true if this element is scrollable.
9545          * @return {Boolean}
9546          */
9547          isScrollable : function(){
9548             var dom = this.dom;
9549             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9550         },
9551
9552         /**
9553          * 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().
9554          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9555          * @param {Number} value The new scroll value
9556          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9557          * @return {Element} this
9558          */
9559
9560         scrollTo : function(side, value, animate){
9561             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9562             if(!animate || !A){
9563                 this.dom[prop] = value;
9564             }else{
9565                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9566                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9567             }
9568             return this;
9569         },
9570
9571         /**
9572          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9573          * within this element's scrollable range.
9574          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9575          * @param {Number} distance How far to scroll the element in pixels
9576          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9577          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9578          * was scrolled as far as it could go.
9579          */
9580          scroll : function(direction, distance, animate){
9581              if(!this.isScrollable()){
9582                  return;
9583              }
9584              var el = this.dom;
9585              var l = el.scrollLeft, t = el.scrollTop;
9586              var w = el.scrollWidth, h = el.scrollHeight;
9587              var cw = el.clientWidth, ch = el.clientHeight;
9588              direction = direction.toLowerCase();
9589              var scrolled = false;
9590              var a = this.preanim(arguments, 2);
9591              switch(direction){
9592                  case "l":
9593                  case "left":
9594                      if(w - l > cw){
9595                          var v = Math.min(l + distance, w-cw);
9596                          this.scrollTo("left", v, a);
9597                          scrolled = true;
9598                      }
9599                      break;
9600                 case "r":
9601                 case "right":
9602                      if(l > 0){
9603                          var v = Math.max(l - distance, 0);
9604                          this.scrollTo("left", v, a);
9605                          scrolled = true;
9606                      }
9607                      break;
9608                 case "t":
9609                 case "top":
9610                 case "up":
9611                      if(t > 0){
9612                          var v = Math.max(t - distance, 0);
9613                          this.scrollTo("top", v, a);
9614                          scrolled = true;
9615                      }
9616                      break;
9617                 case "b":
9618                 case "bottom":
9619                 case "down":
9620                      if(h - t > ch){
9621                          var v = Math.min(t + distance, h-ch);
9622                          this.scrollTo("top", v, a);
9623                          scrolled = true;
9624                      }
9625                      break;
9626              }
9627              return scrolled;
9628         },
9629
9630         /**
9631          * Translates the passed page coordinates into left/top css values for this element
9632          * @param {Number/Array} x The page x or an array containing [x, y]
9633          * @param {Number} y The page y
9634          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9635          */
9636         translatePoints : function(x, y){
9637             if(typeof x == 'object' || x instanceof Array){
9638                 y = x[1]; x = x[0];
9639             }
9640             var p = this.getStyle('position');
9641             var o = this.getXY();
9642
9643             var l = parseInt(this.getStyle('left'), 10);
9644             var t = parseInt(this.getStyle('top'), 10);
9645
9646             if(isNaN(l)){
9647                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9648             }
9649             if(isNaN(t)){
9650                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9651             }
9652
9653             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9654         },
9655
9656         /**
9657          * Returns the current scroll position of the element.
9658          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9659          */
9660         getScroll : function(){
9661             var d = this.dom, doc = document;
9662             if(d == doc || d == doc.body){
9663                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9664                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9665                 return {left: l, top: t};
9666             }else{
9667                 return {left: d.scrollLeft, top: d.scrollTop};
9668             }
9669         },
9670
9671         /**
9672          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9673          * are convert to standard 6 digit hex color.
9674          * @param {String} attr The css attribute
9675          * @param {String} defaultValue The default value to use when a valid color isn't found
9676          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9677          * YUI color anims.
9678          */
9679         getColor : function(attr, defaultValue, prefix){
9680             var v = this.getStyle(attr);
9681             if(!v || v == "transparent" || v == "inherit") {
9682                 return defaultValue;
9683             }
9684             var color = typeof prefix == "undefined" ? "#" : prefix;
9685             if(v.substr(0, 4) == "rgb("){
9686                 var rvs = v.slice(4, v.length -1).split(",");
9687                 for(var i = 0; i < 3; i++){
9688                     var h = parseInt(rvs[i]).toString(16);
9689                     if(h < 16){
9690                         h = "0" + h;
9691                     }
9692                     color += h;
9693                 }
9694             } else {
9695                 if(v.substr(0, 1) == "#"){
9696                     if(v.length == 4) {
9697                         for(var i = 1; i < 4; i++){
9698                             var c = v.charAt(i);
9699                             color +=  c + c;
9700                         }
9701                     }else if(v.length == 7){
9702                         color += v.substr(1);
9703                     }
9704                 }
9705             }
9706             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9707         },
9708
9709         /**
9710          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9711          * gradient background, rounded corners and a 4-way shadow.
9712          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9713          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9714          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9715          * @return {Roo.Element} this
9716          */
9717         boxWrap : function(cls){
9718             cls = cls || 'x-box';
9719             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9720             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9721             return el;
9722         },
9723
9724         /**
9725          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9726          * @param {String} namespace The namespace in which to look for the attribute
9727          * @param {String} name The attribute name
9728          * @return {String} The attribute value
9729          */
9730         getAttributeNS : Roo.isIE ? function(ns, name){
9731             var d = this.dom;
9732             var type = typeof d[ns+":"+name];
9733             if(type != 'undefined' && type != 'unknown'){
9734                 return d[ns+":"+name];
9735             }
9736             return d[name];
9737         } : function(ns, name){
9738             var d = this.dom;
9739             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9740         },
9741         
9742         
9743         /**
9744          * Sets or Returns the value the dom attribute value
9745          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9746          * @param {String} value (optional) The value to set the attribute to
9747          * @return {String} The attribute value
9748          */
9749         attr : function(name){
9750             if (arguments.length > 1) {
9751                 this.dom.setAttribute(name, arguments[1]);
9752                 return arguments[1];
9753             }
9754             if (typeof(name) == 'object') {
9755                 for(var i in name) {
9756                     this.attr(i, name[i]);
9757                 }
9758                 return name;
9759             }
9760             
9761             
9762             if (!this.dom.hasAttribute(name)) {
9763                 return undefined;
9764             }
9765             return this.dom.getAttribute(name);
9766         }
9767         
9768         
9769         
9770     };
9771
9772     var ep = El.prototype;
9773
9774     /**
9775      * Appends an event handler (Shorthand for addListener)
9776      * @param {String}   eventName     The type of event to append
9777      * @param {Function} fn        The method the event invokes
9778      * @param {Object} scope       (optional) The scope (this object) of the fn
9779      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9780      * @method
9781      */
9782     ep.on = ep.addListener;
9783         // backwards compat
9784     ep.mon = ep.addListener;
9785
9786     /**
9787      * Removes an event handler from this element (shorthand for removeListener)
9788      * @param {String} eventName the type of event to remove
9789      * @param {Function} fn the method the event invokes
9790      * @return {Roo.Element} this
9791      * @method
9792      */
9793     ep.un = ep.removeListener;
9794
9795     /**
9796      * true to automatically adjust width and height settings for box-model issues (default to true)
9797      */
9798     ep.autoBoxAdjust = true;
9799
9800     // private
9801     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9802
9803     // private
9804     El.addUnits = function(v, defaultUnit){
9805         if(v === "" || v == "auto"){
9806             return v;
9807         }
9808         if(v === undefined){
9809             return '';
9810         }
9811         if(typeof v == "number" || !El.unitPattern.test(v)){
9812             return v + (defaultUnit || 'px');
9813         }
9814         return v;
9815     };
9816
9817     // special markup used throughout Roo when box wrapping elements
9818     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>';
9819     /**
9820      * Visibility mode constant - Use visibility to hide element
9821      * @static
9822      * @type Number
9823      */
9824     El.VISIBILITY = 1;
9825     /**
9826      * Visibility mode constant - Use display to hide element
9827      * @static
9828      * @type Number
9829      */
9830     El.DISPLAY = 2;
9831
9832     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9833     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9834     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9835
9836
9837
9838     /**
9839      * @private
9840      */
9841     El.cache = {};
9842
9843     var docEl;
9844
9845     /**
9846      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9847      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9848      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9849      * @return {Element} The Element object
9850      * @static
9851      */
9852     El.get = function(el){
9853         var ex, elm, id;
9854         if(!el){ return null; }
9855         if(typeof el == "string"){ // element id
9856             if(!(elm = document.getElementById(el))){
9857                 return null;
9858             }
9859             if(ex = El.cache[el]){
9860                 ex.dom = elm;
9861             }else{
9862                 ex = El.cache[el] = new El(elm);
9863             }
9864             return ex;
9865         }else if(el.tagName){ // dom element
9866             if(!(id = el.id)){
9867                 id = Roo.id(el);
9868             }
9869             if(ex = El.cache[id]){
9870                 ex.dom = el;
9871             }else{
9872                 ex = El.cache[id] = new El(el);
9873             }
9874             return ex;
9875         }else if(el instanceof El){
9876             if(el != docEl){
9877                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9878                                                               // catch case where it hasn't been appended
9879                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9880             }
9881             return el;
9882         }else if(el.isComposite){
9883             return el;
9884         }else if(el instanceof Array){
9885             return El.select(el);
9886         }else if(el == document){
9887             // create a bogus element object representing the document object
9888             if(!docEl){
9889                 var f = function(){};
9890                 f.prototype = El.prototype;
9891                 docEl = new f();
9892                 docEl.dom = document;
9893             }
9894             return docEl;
9895         }
9896         return null;
9897     };
9898
9899     // private
9900     El.uncache = function(el){
9901         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9902             if(a[i]){
9903                 delete El.cache[a[i].id || a[i]];
9904             }
9905         }
9906     };
9907
9908     // private
9909     // Garbage collection - uncache elements/purge listeners on orphaned elements
9910     // so we don't hold a reference and cause the browser to retain them
9911     El.garbageCollect = function(){
9912         if(!Roo.enableGarbageCollector){
9913             clearInterval(El.collectorThread);
9914             return;
9915         }
9916         for(var eid in El.cache){
9917             var el = El.cache[eid], d = el.dom;
9918             // -------------------------------------------------------
9919             // Determining what is garbage:
9920             // -------------------------------------------------------
9921             // !d
9922             // dom node is null, definitely garbage
9923             // -------------------------------------------------------
9924             // !d.parentNode
9925             // no parentNode == direct orphan, definitely garbage
9926             // -------------------------------------------------------
9927             // !d.offsetParent && !document.getElementById(eid)
9928             // display none elements have no offsetParent so we will
9929             // also try to look it up by it's id. However, check
9930             // offsetParent first so we don't do unneeded lookups.
9931             // This enables collection of elements that are not orphans
9932             // directly, but somewhere up the line they have an orphan
9933             // parent.
9934             // -------------------------------------------------------
9935             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9936                 delete El.cache[eid];
9937                 if(d && Roo.enableListenerCollection){
9938                     E.purgeElement(d);
9939                 }
9940             }
9941         }
9942     }
9943     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9944
9945
9946     // dom is optional
9947     El.Flyweight = function(dom){
9948         this.dom = dom;
9949     };
9950     El.Flyweight.prototype = El.prototype;
9951
9952     El._flyweights = {};
9953     /**
9954      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9955      * the dom node can be overwritten by other code.
9956      * @param {String/HTMLElement} el The dom node or id
9957      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9958      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9959      * @static
9960      * @return {Element} The shared Element object
9961      */
9962     El.fly = function(el, named){
9963         named = named || '_global';
9964         el = Roo.getDom(el);
9965         if(!el){
9966             return null;
9967         }
9968         if(!El._flyweights[named]){
9969             El._flyweights[named] = new El.Flyweight();
9970         }
9971         El._flyweights[named].dom = el;
9972         return El._flyweights[named];
9973     };
9974
9975     /**
9976      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9977      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9978      * Shorthand of {@link Roo.Element#get}
9979      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9980      * @return {Element} The Element object
9981      * @member Roo
9982      * @method get
9983      */
9984     Roo.get = El.get;
9985     /**
9986      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9987      * the dom node can be overwritten by other code.
9988      * Shorthand of {@link Roo.Element#fly}
9989      * @param {String/HTMLElement} el The dom node or id
9990      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9991      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9992      * @static
9993      * @return {Element} The shared Element object
9994      * @member Roo
9995      * @method fly
9996      */
9997     Roo.fly = El.fly;
9998
9999     // speedy lookup for elements never to box adjust
10000     var noBoxAdjust = Roo.isStrict ? {
10001         select:1
10002     } : {
10003         input:1, select:1, textarea:1
10004     };
10005     if(Roo.isIE || Roo.isGecko){
10006         noBoxAdjust['button'] = 1;
10007     }
10008
10009
10010     Roo.EventManager.on(window, 'unload', function(){
10011         delete El.cache;
10012         delete El._flyweights;
10013     });
10014 })();
10015
10016
10017
10018
10019 if(Roo.DomQuery){
10020     Roo.Element.selectorFunction = Roo.DomQuery.select;
10021 }
10022
10023 Roo.Element.select = function(selector, unique, root){
10024     var els;
10025     if(typeof selector == "string"){
10026         els = Roo.Element.selectorFunction(selector, root);
10027     }else if(selector.length !== undefined){
10028         els = selector;
10029     }else{
10030         throw "Invalid selector";
10031     }
10032     if(unique === true){
10033         return new Roo.CompositeElement(els);
10034     }else{
10035         return new Roo.CompositeElementLite(els);
10036     }
10037 };
10038 /**
10039  * Selects elements based on the passed CSS selector to enable working on them as 1.
10040  * @param {String/Array} selector The CSS selector or an array of elements
10041  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10042  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10043  * @return {CompositeElementLite/CompositeElement}
10044  * @member Roo
10045  * @method select
10046  */
10047 Roo.select = Roo.Element.select;
10048
10049
10050
10051
10052
10053
10054
10055
10056
10057
10058
10059
10060
10061
10062 /*
10063  * Based on:
10064  * Ext JS Library 1.1.1
10065  * Copyright(c) 2006-2007, Ext JS, LLC.
10066  *
10067  * Originally Released Under LGPL - original licence link has changed is not relivant.
10068  *
10069  * Fork - LGPL
10070  * <script type="text/javascript">
10071  */
10072
10073
10074
10075 //Notifies Element that fx methods are available
10076 Roo.enableFx = true;
10077
10078 /**
10079  * @class Roo.Fx
10080  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10081  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10082  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10083  * Element effects to work.</p><br/>
10084  *
10085  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10086  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10087  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10088  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10089  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10090  * expected results and should be done with care.</p><br/>
10091  *
10092  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10093  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10094 <pre>
10095 Value  Description
10096 -----  -----------------------------
10097 tl     The top left corner
10098 t      The center of the top edge
10099 tr     The top right corner
10100 l      The center of the left edge
10101 r      The center of the right edge
10102 bl     The bottom left corner
10103 b      The center of the bottom edge
10104 br     The bottom right corner
10105 </pre>
10106  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10107  * below are common options that can be passed to any Fx method.</b>
10108  * @cfg {Function} callback A function called when the effect is finished
10109  * @cfg {Object} scope The scope of the effect function
10110  * @cfg {String} easing A valid Easing value for the effect
10111  * @cfg {String} afterCls A css class to apply after the effect
10112  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10113  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10114  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10115  * effects that end with the element being visually hidden, ignored otherwise)
10116  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10117  * a function which returns such a specification that will be applied to the Element after the effect finishes
10118  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10119  * @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
10120  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10121  */
10122 Roo.Fx = {
10123         /**
10124          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10125          * origin for the slide effect.  This function automatically handles wrapping the element with
10126          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10127          * Usage:
10128          *<pre><code>
10129 // default: slide the element in from the top
10130 el.slideIn();
10131
10132 // custom: slide the element in from the right with a 2-second duration
10133 el.slideIn('r', { duration: 2 });
10134
10135 // common config options shown with default values
10136 el.slideIn('t', {
10137     easing: 'easeOut',
10138     duration: .5
10139 });
10140 </code></pre>
10141          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10142          * @param {Object} options (optional) Object literal with any of the Fx config options
10143          * @return {Roo.Element} The Element
10144          */
10145     slideIn : function(anchor, o){
10146         var el = this.getFxEl();
10147         o = o || {};
10148
10149         el.queueFx(o, function(){
10150
10151             anchor = anchor || "t";
10152
10153             // fix display to visibility
10154             this.fixDisplay();
10155
10156             // restore values after effect
10157             var r = this.getFxRestore();
10158             var b = this.getBox();
10159             // fixed size for slide
10160             this.setSize(b);
10161
10162             // wrap if needed
10163             var wrap = this.fxWrap(r.pos, o, "hidden");
10164
10165             var st = this.dom.style;
10166             st.visibility = "visible";
10167             st.position = "absolute";
10168
10169             // clear out temp styles after slide and unwrap
10170             var after = function(){
10171                 el.fxUnwrap(wrap, r.pos, o);
10172                 st.width = r.width;
10173                 st.height = r.height;
10174                 el.afterFx(o);
10175             };
10176             // time to calc the positions
10177             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10178
10179             switch(anchor.toLowerCase()){
10180                 case "t":
10181                     wrap.setSize(b.width, 0);
10182                     st.left = st.bottom = "0";
10183                     a = {height: bh};
10184                 break;
10185                 case "l":
10186                     wrap.setSize(0, b.height);
10187                     st.right = st.top = "0";
10188                     a = {width: bw};
10189                 break;
10190                 case "r":
10191                     wrap.setSize(0, b.height);
10192                     wrap.setX(b.right);
10193                     st.left = st.top = "0";
10194                     a = {width: bw, points: pt};
10195                 break;
10196                 case "b":
10197                     wrap.setSize(b.width, 0);
10198                     wrap.setY(b.bottom);
10199                     st.left = st.top = "0";
10200                     a = {height: bh, points: pt};
10201                 break;
10202                 case "tl":
10203                     wrap.setSize(0, 0);
10204                     st.right = st.bottom = "0";
10205                     a = {width: bw, height: bh};
10206                 break;
10207                 case "bl":
10208                     wrap.setSize(0, 0);
10209                     wrap.setY(b.y+b.height);
10210                     st.right = st.top = "0";
10211                     a = {width: bw, height: bh, points: pt};
10212                 break;
10213                 case "br":
10214                     wrap.setSize(0, 0);
10215                     wrap.setXY([b.right, b.bottom]);
10216                     st.left = st.top = "0";
10217                     a = {width: bw, height: bh, points: pt};
10218                 break;
10219                 case "tr":
10220                     wrap.setSize(0, 0);
10221                     wrap.setX(b.x+b.width);
10222                     st.left = st.bottom = "0";
10223                     a = {width: bw, height: bh, points: pt};
10224                 break;
10225             }
10226             this.dom.style.visibility = "visible";
10227             wrap.show();
10228
10229             arguments.callee.anim = wrap.fxanim(a,
10230                 o,
10231                 'motion',
10232                 .5,
10233                 'easeOut', after);
10234         });
10235         return this;
10236     },
10237     
10238         /**
10239          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10240          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10241          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10242          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10243          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10244          * Usage:
10245          *<pre><code>
10246 // default: slide the element out to the top
10247 el.slideOut();
10248
10249 // custom: slide the element out to the right with a 2-second duration
10250 el.slideOut('r', { duration: 2 });
10251
10252 // common config options shown with default values
10253 el.slideOut('t', {
10254     easing: 'easeOut',
10255     duration: .5,
10256     remove: false,
10257     useDisplay: false
10258 });
10259 </code></pre>
10260          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10261          * @param {Object} options (optional) Object literal with any of the Fx config options
10262          * @return {Roo.Element} The Element
10263          */
10264     slideOut : function(anchor, o){
10265         var el = this.getFxEl();
10266         o = o || {};
10267
10268         el.queueFx(o, function(){
10269
10270             anchor = anchor || "t";
10271
10272             // restore values after effect
10273             var r = this.getFxRestore();
10274             
10275             var b = this.getBox();
10276             // fixed size for slide
10277             this.setSize(b);
10278
10279             // wrap if needed
10280             var wrap = this.fxWrap(r.pos, o, "visible");
10281
10282             var st = this.dom.style;
10283             st.visibility = "visible";
10284             st.position = "absolute";
10285
10286             wrap.setSize(b);
10287
10288             var after = function(){
10289                 if(o.useDisplay){
10290                     el.setDisplayed(false);
10291                 }else{
10292                     el.hide();
10293                 }
10294
10295                 el.fxUnwrap(wrap, r.pos, o);
10296
10297                 st.width = r.width;
10298                 st.height = r.height;
10299
10300                 el.afterFx(o);
10301             };
10302
10303             var a, zero = {to: 0};
10304             switch(anchor.toLowerCase()){
10305                 case "t":
10306                     st.left = st.bottom = "0";
10307                     a = {height: zero};
10308                 break;
10309                 case "l":
10310                     st.right = st.top = "0";
10311                     a = {width: zero};
10312                 break;
10313                 case "r":
10314                     st.left = st.top = "0";
10315                     a = {width: zero, points: {to:[b.right, b.y]}};
10316                 break;
10317                 case "b":
10318                     st.left = st.top = "0";
10319                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10320                 break;
10321                 case "tl":
10322                     st.right = st.bottom = "0";
10323                     a = {width: zero, height: zero};
10324                 break;
10325                 case "bl":
10326                     st.right = st.top = "0";
10327                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10328                 break;
10329                 case "br":
10330                     st.left = st.top = "0";
10331                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10332                 break;
10333                 case "tr":
10334                     st.left = st.bottom = "0";
10335                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10336                 break;
10337             }
10338
10339             arguments.callee.anim = wrap.fxanim(a,
10340                 o,
10341                 'motion',
10342                 .5,
10343                 "easeOut", after);
10344         });
10345         return this;
10346     },
10347
10348         /**
10349          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10350          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10351          * The element must be removed from the DOM using the 'remove' config option if desired.
10352          * Usage:
10353          *<pre><code>
10354 // default
10355 el.puff();
10356
10357 // common config options shown with default values
10358 el.puff({
10359     easing: 'easeOut',
10360     duration: .5,
10361     remove: false,
10362     useDisplay: false
10363 });
10364 </code></pre>
10365          * @param {Object} options (optional) Object literal with any of the Fx config options
10366          * @return {Roo.Element} The Element
10367          */
10368     puff : function(o){
10369         var el = this.getFxEl();
10370         o = o || {};
10371
10372         el.queueFx(o, function(){
10373             this.clearOpacity();
10374             this.show();
10375
10376             // restore values after effect
10377             var r = this.getFxRestore();
10378             var st = this.dom.style;
10379
10380             var after = function(){
10381                 if(o.useDisplay){
10382                     el.setDisplayed(false);
10383                 }else{
10384                     el.hide();
10385                 }
10386
10387                 el.clearOpacity();
10388
10389                 el.setPositioning(r.pos);
10390                 st.width = r.width;
10391                 st.height = r.height;
10392                 st.fontSize = '';
10393                 el.afterFx(o);
10394             };
10395
10396             var width = this.getWidth();
10397             var height = this.getHeight();
10398
10399             arguments.callee.anim = this.fxanim({
10400                     width : {to: this.adjustWidth(width * 2)},
10401                     height : {to: this.adjustHeight(height * 2)},
10402                     points : {by: [-(width * .5), -(height * .5)]},
10403                     opacity : {to: 0},
10404                     fontSize: {to:200, unit: "%"}
10405                 },
10406                 o,
10407                 'motion',
10408                 .5,
10409                 "easeOut", after);
10410         });
10411         return this;
10412     },
10413
10414         /**
10415          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10416          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10417          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10418          * Usage:
10419          *<pre><code>
10420 // default
10421 el.switchOff();
10422
10423 // all config options shown with default values
10424 el.switchOff({
10425     easing: 'easeIn',
10426     duration: .3,
10427     remove: false,
10428     useDisplay: false
10429 });
10430 </code></pre>
10431          * @param {Object} options (optional) Object literal with any of the Fx config options
10432          * @return {Roo.Element} The Element
10433          */
10434     switchOff : function(o){
10435         var el = this.getFxEl();
10436         o = o || {};
10437
10438         el.queueFx(o, function(){
10439             this.clearOpacity();
10440             this.clip();
10441
10442             // restore values after effect
10443             var r = this.getFxRestore();
10444             var st = this.dom.style;
10445
10446             var after = function(){
10447                 if(o.useDisplay){
10448                     el.setDisplayed(false);
10449                 }else{
10450                     el.hide();
10451                 }
10452
10453                 el.clearOpacity();
10454                 el.setPositioning(r.pos);
10455                 st.width = r.width;
10456                 st.height = r.height;
10457
10458                 el.afterFx(o);
10459             };
10460
10461             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10462                 this.clearOpacity();
10463                 (function(){
10464                     this.fxanim({
10465                         height:{to:1},
10466                         points:{by:[0, this.getHeight() * .5]}
10467                     }, o, 'motion', 0.3, 'easeIn', after);
10468                 }).defer(100, this);
10469             });
10470         });
10471         return this;
10472     },
10473
10474     /**
10475      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10476      * changed using the "attr" config option) and then fading back to the original color. If no original
10477      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10478      * Usage:
10479 <pre><code>
10480 // default: highlight background to yellow
10481 el.highlight();
10482
10483 // custom: highlight foreground text to blue for 2 seconds
10484 el.highlight("0000ff", { attr: 'color', duration: 2 });
10485
10486 // common config options shown with default values
10487 el.highlight("ffff9c", {
10488     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10489     endColor: (current color) or "ffffff",
10490     easing: 'easeIn',
10491     duration: 1
10492 });
10493 </code></pre>
10494      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10495      * @param {Object} options (optional) Object literal with any of the Fx config options
10496      * @return {Roo.Element} The Element
10497      */ 
10498     highlight : function(color, o){
10499         var el = this.getFxEl();
10500         o = o || {};
10501
10502         el.queueFx(o, function(){
10503             color = color || "ffff9c";
10504             attr = o.attr || "backgroundColor";
10505
10506             this.clearOpacity();
10507             this.show();
10508
10509             var origColor = this.getColor(attr);
10510             var restoreColor = this.dom.style[attr];
10511             endColor = (o.endColor || origColor) || "ffffff";
10512
10513             var after = function(){
10514                 el.dom.style[attr] = restoreColor;
10515                 el.afterFx(o);
10516             };
10517
10518             var a = {};
10519             a[attr] = {from: color, to: endColor};
10520             arguments.callee.anim = this.fxanim(a,
10521                 o,
10522                 'color',
10523                 1,
10524                 'easeIn', after);
10525         });
10526         return this;
10527     },
10528
10529    /**
10530     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10531     * Usage:
10532 <pre><code>
10533 // default: a single light blue ripple
10534 el.frame();
10535
10536 // custom: 3 red ripples lasting 3 seconds total
10537 el.frame("ff0000", 3, { duration: 3 });
10538
10539 // common config options shown with default values
10540 el.frame("C3DAF9", 1, {
10541     duration: 1 //duration of entire animation (not each individual ripple)
10542     // Note: Easing is not configurable and will be ignored if included
10543 });
10544 </code></pre>
10545     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10546     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10547     * @param {Object} options (optional) Object literal with any of the Fx config options
10548     * @return {Roo.Element} The Element
10549     */
10550     frame : function(color, count, o){
10551         var el = this.getFxEl();
10552         o = o || {};
10553
10554         el.queueFx(o, function(){
10555             color = color || "#C3DAF9";
10556             if(color.length == 6){
10557                 color = "#" + color;
10558             }
10559             count = count || 1;
10560             duration = o.duration || 1;
10561             this.show();
10562
10563             var b = this.getBox();
10564             var animFn = function(){
10565                 var proxy = this.createProxy({
10566
10567                      style:{
10568                         visbility:"hidden",
10569                         position:"absolute",
10570                         "z-index":"35000", // yee haw
10571                         border:"0px solid " + color
10572                      }
10573                   });
10574                 var scale = Roo.isBorderBox ? 2 : 1;
10575                 proxy.animate({
10576                     top:{from:b.y, to:b.y - 20},
10577                     left:{from:b.x, to:b.x - 20},
10578                     borderWidth:{from:0, to:10},
10579                     opacity:{from:1, to:0},
10580                     height:{from:b.height, to:(b.height + (20*scale))},
10581                     width:{from:b.width, to:(b.width + (20*scale))}
10582                 }, duration, function(){
10583                     proxy.remove();
10584                 });
10585                 if(--count > 0){
10586                      animFn.defer((duration/2)*1000, this);
10587                 }else{
10588                     el.afterFx(o);
10589                 }
10590             };
10591             animFn.call(this);
10592         });
10593         return this;
10594     },
10595
10596    /**
10597     * Creates a pause before any subsequent queued effects begin.  If there are
10598     * no effects queued after the pause it will have no effect.
10599     * Usage:
10600 <pre><code>
10601 el.pause(1);
10602 </code></pre>
10603     * @param {Number} seconds The length of time to pause (in seconds)
10604     * @return {Roo.Element} The Element
10605     */
10606     pause : function(seconds){
10607         var el = this.getFxEl();
10608         var o = {};
10609
10610         el.queueFx(o, function(){
10611             setTimeout(function(){
10612                 el.afterFx(o);
10613             }, seconds * 1000);
10614         });
10615         return this;
10616     },
10617
10618    /**
10619     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10620     * using the "endOpacity" config option.
10621     * Usage:
10622 <pre><code>
10623 // default: fade in from opacity 0 to 100%
10624 el.fadeIn();
10625
10626 // custom: fade in from opacity 0 to 75% over 2 seconds
10627 el.fadeIn({ endOpacity: .75, duration: 2});
10628
10629 // common config options shown with default values
10630 el.fadeIn({
10631     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10632     easing: 'easeOut',
10633     duration: .5
10634 });
10635 </code></pre>
10636     * @param {Object} options (optional) Object literal with any of the Fx config options
10637     * @return {Roo.Element} The Element
10638     */
10639     fadeIn : function(o){
10640         var el = this.getFxEl();
10641         o = o || {};
10642         el.queueFx(o, function(){
10643             this.setOpacity(0);
10644             this.fixDisplay();
10645             this.dom.style.visibility = 'visible';
10646             var to = o.endOpacity || 1;
10647             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10648                 o, null, .5, "easeOut", function(){
10649                 if(to == 1){
10650                     this.clearOpacity();
10651                 }
10652                 el.afterFx(o);
10653             });
10654         });
10655         return this;
10656     },
10657
10658    /**
10659     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10660     * using the "endOpacity" config option.
10661     * Usage:
10662 <pre><code>
10663 // default: fade out from the element's current opacity to 0
10664 el.fadeOut();
10665
10666 // custom: fade out from the element's current opacity to 25% over 2 seconds
10667 el.fadeOut({ endOpacity: .25, duration: 2});
10668
10669 // common config options shown with default values
10670 el.fadeOut({
10671     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10672     easing: 'easeOut',
10673     duration: .5
10674     remove: false,
10675     useDisplay: false
10676 });
10677 </code></pre>
10678     * @param {Object} options (optional) Object literal with any of the Fx config options
10679     * @return {Roo.Element} The Element
10680     */
10681     fadeOut : function(o){
10682         var el = this.getFxEl();
10683         o = o || {};
10684         el.queueFx(o, function(){
10685             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10686                 o, null, .5, "easeOut", function(){
10687                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10688                      this.dom.style.display = "none";
10689                 }else{
10690                      this.dom.style.visibility = "hidden";
10691                 }
10692                 this.clearOpacity();
10693                 el.afterFx(o);
10694             });
10695         });
10696         return this;
10697     },
10698
10699    /**
10700     * Animates the transition of an element's dimensions from a starting height/width
10701     * to an ending height/width.
10702     * Usage:
10703 <pre><code>
10704 // change height and width to 100x100 pixels
10705 el.scale(100, 100);
10706
10707 // common config options shown with default values.  The height and width will default to
10708 // the element's existing values if passed as null.
10709 el.scale(
10710     [element's width],
10711     [element's height], {
10712     easing: 'easeOut',
10713     duration: .35
10714 });
10715 </code></pre>
10716     * @param {Number} width  The new width (pass undefined to keep the original width)
10717     * @param {Number} height  The new height (pass undefined to keep the original height)
10718     * @param {Object} options (optional) Object literal with any of the Fx config options
10719     * @return {Roo.Element} The Element
10720     */
10721     scale : function(w, h, o){
10722         this.shift(Roo.apply({}, o, {
10723             width: w,
10724             height: h
10725         }));
10726         return this;
10727     },
10728
10729    /**
10730     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10731     * Any of these properties not specified in the config object will not be changed.  This effect 
10732     * requires that at least one new dimension, position or opacity setting must be passed in on
10733     * the config object in order for the function to have any effect.
10734     * Usage:
10735 <pre><code>
10736 // slide the element horizontally to x position 200 while changing the height and opacity
10737 el.shift({ x: 200, height: 50, opacity: .8 });
10738
10739 // common config options shown with default values.
10740 el.shift({
10741     width: [element's width],
10742     height: [element's height],
10743     x: [element's x position],
10744     y: [element's y position],
10745     opacity: [element's opacity],
10746     easing: 'easeOut',
10747     duration: .35
10748 });
10749 </code></pre>
10750     * @param {Object} options  Object literal with any of the Fx config options
10751     * @return {Roo.Element} The Element
10752     */
10753     shift : function(o){
10754         var el = this.getFxEl();
10755         o = o || {};
10756         el.queueFx(o, function(){
10757             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10758             if(w !== undefined){
10759                 a.width = {to: this.adjustWidth(w)};
10760             }
10761             if(h !== undefined){
10762                 a.height = {to: this.adjustHeight(h)};
10763             }
10764             if(x !== undefined || y !== undefined){
10765                 a.points = {to: [
10766                     x !== undefined ? x : this.getX(),
10767                     y !== undefined ? y : this.getY()
10768                 ]};
10769             }
10770             if(op !== undefined){
10771                 a.opacity = {to: op};
10772             }
10773             if(o.xy !== undefined){
10774                 a.points = {to: o.xy};
10775             }
10776             arguments.callee.anim = this.fxanim(a,
10777                 o, 'motion', .35, "easeOut", function(){
10778                 el.afterFx(o);
10779             });
10780         });
10781         return this;
10782     },
10783
10784         /**
10785          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10786          * ending point of the effect.
10787          * Usage:
10788          *<pre><code>
10789 // default: slide the element downward while fading out
10790 el.ghost();
10791
10792 // custom: slide the element out to the right with a 2-second duration
10793 el.ghost('r', { duration: 2 });
10794
10795 // common config options shown with default values
10796 el.ghost('b', {
10797     easing: 'easeOut',
10798     duration: .5
10799     remove: false,
10800     useDisplay: false
10801 });
10802 </code></pre>
10803          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10804          * @param {Object} options (optional) Object literal with any of the Fx config options
10805          * @return {Roo.Element} The Element
10806          */
10807     ghost : function(anchor, o){
10808         var el = this.getFxEl();
10809         o = o || {};
10810
10811         el.queueFx(o, function(){
10812             anchor = anchor || "b";
10813
10814             // restore values after effect
10815             var r = this.getFxRestore();
10816             var w = this.getWidth(),
10817                 h = this.getHeight();
10818
10819             var st = this.dom.style;
10820
10821             var after = function(){
10822                 if(o.useDisplay){
10823                     el.setDisplayed(false);
10824                 }else{
10825                     el.hide();
10826                 }
10827
10828                 el.clearOpacity();
10829                 el.setPositioning(r.pos);
10830                 st.width = r.width;
10831                 st.height = r.height;
10832
10833                 el.afterFx(o);
10834             };
10835
10836             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10837             switch(anchor.toLowerCase()){
10838                 case "t":
10839                     pt.by = [0, -h];
10840                 break;
10841                 case "l":
10842                     pt.by = [-w, 0];
10843                 break;
10844                 case "r":
10845                     pt.by = [w, 0];
10846                 break;
10847                 case "b":
10848                     pt.by = [0, h];
10849                 break;
10850                 case "tl":
10851                     pt.by = [-w, -h];
10852                 break;
10853                 case "bl":
10854                     pt.by = [-w, h];
10855                 break;
10856                 case "br":
10857                     pt.by = [w, h];
10858                 break;
10859                 case "tr":
10860                     pt.by = [w, -h];
10861                 break;
10862             }
10863
10864             arguments.callee.anim = this.fxanim(a,
10865                 o,
10866                 'motion',
10867                 .5,
10868                 "easeOut", after);
10869         });
10870         return this;
10871     },
10872
10873         /**
10874          * Ensures that all effects queued after syncFx is called on the element are
10875          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10876          * @return {Roo.Element} The Element
10877          */
10878     syncFx : function(){
10879         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10880             block : false,
10881             concurrent : true,
10882             stopFx : false
10883         });
10884         return this;
10885     },
10886
10887         /**
10888          * Ensures that all effects queued after sequenceFx is called on the element are
10889          * run in sequence.  This is the opposite of {@link #syncFx}.
10890          * @return {Roo.Element} The Element
10891          */
10892     sequenceFx : function(){
10893         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10894             block : false,
10895             concurrent : false,
10896             stopFx : false
10897         });
10898         return this;
10899     },
10900
10901         /* @private */
10902     nextFx : function(){
10903         var ef = this.fxQueue[0];
10904         if(ef){
10905             ef.call(this);
10906         }
10907     },
10908
10909         /**
10910          * Returns true if the element has any effects actively running or queued, else returns false.
10911          * @return {Boolean} True if element has active effects, else false
10912          */
10913     hasActiveFx : function(){
10914         return this.fxQueue && this.fxQueue[0];
10915     },
10916
10917         /**
10918          * Stops any running effects and clears the element's internal effects queue if it contains
10919          * any additional effects that haven't started yet.
10920          * @return {Roo.Element} The Element
10921          */
10922     stopFx : function(){
10923         if(this.hasActiveFx()){
10924             var cur = this.fxQueue[0];
10925             if(cur && cur.anim && cur.anim.isAnimated()){
10926                 this.fxQueue = [cur]; // clear out others
10927                 cur.anim.stop(true);
10928             }
10929         }
10930         return this;
10931     },
10932
10933         /* @private */
10934     beforeFx : function(o){
10935         if(this.hasActiveFx() && !o.concurrent){
10936            if(o.stopFx){
10937                this.stopFx();
10938                return true;
10939            }
10940            return false;
10941         }
10942         return true;
10943     },
10944
10945         /**
10946          * Returns true if the element is currently blocking so that no other effect can be queued
10947          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10948          * used to ensure that an effect initiated by a user action runs to completion prior to the
10949          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10950          * @return {Boolean} True if blocking, else false
10951          */
10952     hasFxBlock : function(){
10953         var q = this.fxQueue;
10954         return q && q[0] && q[0].block;
10955     },
10956
10957         /* @private */
10958     queueFx : function(o, fn){
10959         if(!this.fxQueue){
10960             this.fxQueue = [];
10961         }
10962         if(!this.hasFxBlock()){
10963             Roo.applyIf(o, this.fxDefaults);
10964             if(!o.concurrent){
10965                 var run = this.beforeFx(o);
10966                 fn.block = o.block;
10967                 this.fxQueue.push(fn);
10968                 if(run){
10969                     this.nextFx();
10970                 }
10971             }else{
10972                 fn.call(this);
10973             }
10974         }
10975         return this;
10976     },
10977
10978         /* @private */
10979     fxWrap : function(pos, o, vis){
10980         var wrap;
10981         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10982             var wrapXY;
10983             if(o.fixPosition){
10984                 wrapXY = this.getXY();
10985             }
10986             var div = document.createElement("div");
10987             div.style.visibility = vis;
10988             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10989             wrap.setPositioning(pos);
10990             if(wrap.getStyle("position") == "static"){
10991                 wrap.position("relative");
10992             }
10993             this.clearPositioning('auto');
10994             wrap.clip();
10995             wrap.dom.appendChild(this.dom);
10996             if(wrapXY){
10997                 wrap.setXY(wrapXY);
10998             }
10999         }
11000         return wrap;
11001     },
11002
11003         /* @private */
11004     fxUnwrap : function(wrap, pos, o){
11005         this.clearPositioning();
11006         this.setPositioning(pos);
11007         if(!o.wrap){
11008             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11009             wrap.remove();
11010         }
11011     },
11012
11013         /* @private */
11014     getFxRestore : function(){
11015         var st = this.dom.style;
11016         return {pos: this.getPositioning(), width: st.width, height : st.height};
11017     },
11018
11019         /* @private */
11020     afterFx : function(o){
11021         if(o.afterStyle){
11022             this.applyStyles(o.afterStyle);
11023         }
11024         if(o.afterCls){
11025             this.addClass(o.afterCls);
11026         }
11027         if(o.remove === true){
11028             this.remove();
11029         }
11030         Roo.callback(o.callback, o.scope, [this]);
11031         if(!o.concurrent){
11032             this.fxQueue.shift();
11033             this.nextFx();
11034         }
11035     },
11036
11037         /* @private */
11038     getFxEl : function(){ // support for composite element fx
11039         return Roo.get(this.dom);
11040     },
11041
11042         /* @private */
11043     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11044         animType = animType || 'run';
11045         opt = opt || {};
11046         var anim = Roo.lib.Anim[animType](
11047             this.dom, args,
11048             (opt.duration || defaultDur) || .35,
11049             (opt.easing || defaultEase) || 'easeOut',
11050             function(){
11051                 Roo.callback(cb, this);
11052             },
11053             this
11054         );
11055         opt.anim = anim;
11056         return anim;
11057     }
11058 };
11059
11060 // backwords compat
11061 Roo.Fx.resize = Roo.Fx.scale;
11062
11063 //When included, Roo.Fx is automatically applied to Element so that all basic
11064 //effects are available directly via the Element API
11065 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11066  * Based on:
11067  * Ext JS Library 1.1.1
11068  * Copyright(c) 2006-2007, Ext JS, LLC.
11069  *
11070  * Originally Released Under LGPL - original licence link has changed is not relivant.
11071  *
11072  * Fork - LGPL
11073  * <script type="text/javascript">
11074  */
11075
11076
11077 /**
11078  * @class Roo.CompositeElement
11079  * Standard composite class. Creates a Roo.Element for every element in the collection.
11080  * <br><br>
11081  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11082  * actions will be performed on all the elements in this collection.</b>
11083  * <br><br>
11084  * All methods return <i>this</i> and can be chained.
11085  <pre><code>
11086  var els = Roo.select("#some-el div.some-class", true);
11087  // or select directly from an existing element
11088  var el = Roo.get('some-el');
11089  el.select('div.some-class', true);
11090
11091  els.setWidth(100); // all elements become 100 width
11092  els.hide(true); // all elements fade out and hide
11093  // or
11094  els.setWidth(100).hide(true);
11095  </code></pre>
11096  */
11097 Roo.CompositeElement = function(els){
11098     this.elements = [];
11099     this.addElements(els);
11100 };
11101 Roo.CompositeElement.prototype = {
11102     isComposite: true,
11103     addElements : function(els){
11104         if(!els) {
11105             return this;
11106         }
11107         if(typeof els == "string"){
11108             els = Roo.Element.selectorFunction(els);
11109         }
11110         var yels = this.elements;
11111         var index = yels.length-1;
11112         for(var i = 0, len = els.length; i < len; i++) {
11113                 yels[++index] = Roo.get(els[i]);
11114         }
11115         return this;
11116     },
11117
11118     /**
11119     * Clears this composite and adds the elements returned by the passed selector.
11120     * @param {String/Array} els A string CSS selector, an array of elements or an element
11121     * @return {CompositeElement} this
11122     */
11123     fill : function(els){
11124         this.elements = [];
11125         this.add(els);
11126         return this;
11127     },
11128
11129     /**
11130     * Filters this composite to only elements that match the passed selector.
11131     * @param {String} selector A string CSS selector
11132     * @param {Boolean} inverse return inverse filter (not matches)
11133     * @return {CompositeElement} this
11134     */
11135     filter : function(selector, inverse){
11136         var els = [];
11137         inverse = inverse || false;
11138         this.each(function(el){
11139             var match = inverse ? !el.is(selector) : el.is(selector);
11140             if(match){
11141                 els[els.length] = el.dom;
11142             }
11143         });
11144         this.fill(els);
11145         return this;
11146     },
11147
11148     invoke : function(fn, args){
11149         var els = this.elements;
11150         for(var i = 0, len = els.length; i < len; i++) {
11151                 Roo.Element.prototype[fn].apply(els[i], args);
11152         }
11153         return this;
11154     },
11155     /**
11156     * Adds elements to this composite.
11157     * @param {String/Array} els A string CSS selector, an array of elements or an element
11158     * @return {CompositeElement} this
11159     */
11160     add : function(els){
11161         if(typeof els == "string"){
11162             this.addElements(Roo.Element.selectorFunction(els));
11163         }else if(els.length !== undefined){
11164             this.addElements(els);
11165         }else{
11166             this.addElements([els]);
11167         }
11168         return this;
11169     },
11170     /**
11171     * Calls the passed function passing (el, this, index) for each element in this composite.
11172     * @param {Function} fn The function to call
11173     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11174     * @return {CompositeElement} this
11175     */
11176     each : function(fn, scope){
11177         var els = this.elements;
11178         for(var i = 0, len = els.length; i < len; i++){
11179             if(fn.call(scope || els[i], els[i], this, i) === false) {
11180                 break;
11181             }
11182         }
11183         return this;
11184     },
11185
11186     /**
11187      * Returns the Element object at the specified index
11188      * @param {Number} index
11189      * @return {Roo.Element}
11190      */
11191     item : function(index){
11192         return this.elements[index] || null;
11193     },
11194
11195     /**
11196      * Returns the first Element
11197      * @return {Roo.Element}
11198      */
11199     first : function(){
11200         return this.item(0);
11201     },
11202
11203     /**
11204      * Returns the last Element
11205      * @return {Roo.Element}
11206      */
11207     last : function(){
11208         return this.item(this.elements.length-1);
11209     },
11210
11211     /**
11212      * Returns the number of elements in this composite
11213      * @return Number
11214      */
11215     getCount : function(){
11216         return this.elements.length;
11217     },
11218
11219     /**
11220      * Returns true if this composite contains the passed element
11221      * @return Boolean
11222      */
11223     contains : function(el){
11224         return this.indexOf(el) !== -1;
11225     },
11226
11227     /**
11228      * Returns true if this composite contains the passed element
11229      * @return Boolean
11230      */
11231     indexOf : function(el){
11232         return this.elements.indexOf(Roo.get(el));
11233     },
11234
11235
11236     /**
11237     * Removes the specified element(s).
11238     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11239     * or an array of any of those.
11240     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11241     * @return {CompositeElement} this
11242     */
11243     removeElement : function(el, removeDom){
11244         if(el instanceof Array){
11245             for(var i = 0, len = el.length; i < len; i++){
11246                 this.removeElement(el[i]);
11247             }
11248             return this;
11249         }
11250         var index = typeof el == 'number' ? el : this.indexOf(el);
11251         if(index !== -1){
11252             if(removeDom){
11253                 var d = this.elements[index];
11254                 if(d.dom){
11255                     d.remove();
11256                 }else{
11257                     d.parentNode.removeChild(d);
11258                 }
11259             }
11260             this.elements.splice(index, 1);
11261         }
11262         return this;
11263     },
11264
11265     /**
11266     * Replaces the specified element with the passed element.
11267     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11268     * to replace.
11269     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11270     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11271     * @return {CompositeElement} this
11272     */
11273     replaceElement : function(el, replacement, domReplace){
11274         var index = typeof el == 'number' ? el : this.indexOf(el);
11275         if(index !== -1){
11276             if(domReplace){
11277                 this.elements[index].replaceWith(replacement);
11278             }else{
11279                 this.elements.splice(index, 1, Roo.get(replacement))
11280             }
11281         }
11282         return this;
11283     },
11284
11285     /**
11286      * Removes all elements.
11287      */
11288     clear : function(){
11289         this.elements = [];
11290     }
11291 };
11292 (function(){
11293     Roo.CompositeElement.createCall = function(proto, fnName){
11294         if(!proto[fnName]){
11295             proto[fnName] = function(){
11296                 return this.invoke(fnName, arguments);
11297             };
11298         }
11299     };
11300     for(var fnName in Roo.Element.prototype){
11301         if(typeof Roo.Element.prototype[fnName] == "function"){
11302             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11303         }
11304     };
11305 })();
11306 /*
11307  * Based on:
11308  * Ext JS Library 1.1.1
11309  * Copyright(c) 2006-2007, Ext JS, LLC.
11310  *
11311  * Originally Released Under LGPL - original licence link has changed is not relivant.
11312  *
11313  * Fork - LGPL
11314  * <script type="text/javascript">
11315  */
11316
11317 /**
11318  * @class Roo.CompositeElementLite
11319  * @extends Roo.CompositeElement
11320  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11321  <pre><code>
11322  var els = Roo.select("#some-el div.some-class");
11323  // or select directly from an existing element
11324  var el = Roo.get('some-el');
11325  el.select('div.some-class');
11326
11327  els.setWidth(100); // all elements become 100 width
11328  els.hide(true); // all elements fade out and hide
11329  // or
11330  els.setWidth(100).hide(true);
11331  </code></pre><br><br>
11332  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11333  * actions will be performed on all the elements in this collection.</b>
11334  */
11335 Roo.CompositeElementLite = function(els){
11336     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11337     this.el = new Roo.Element.Flyweight();
11338 };
11339 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11340     addElements : function(els){
11341         if(els){
11342             if(els instanceof Array){
11343                 this.elements = this.elements.concat(els);
11344             }else{
11345                 var yels = this.elements;
11346                 var index = yels.length-1;
11347                 for(var i = 0, len = els.length; i < len; i++) {
11348                     yels[++index] = els[i];
11349                 }
11350             }
11351         }
11352         return this;
11353     },
11354     invoke : function(fn, args){
11355         var els = this.elements;
11356         var el = this.el;
11357         for(var i = 0, len = els.length; i < len; i++) {
11358             el.dom = els[i];
11359                 Roo.Element.prototype[fn].apply(el, args);
11360         }
11361         return this;
11362     },
11363     /**
11364      * Returns a flyweight Element of the dom element object at the specified index
11365      * @param {Number} index
11366      * @return {Roo.Element}
11367      */
11368     item : function(index){
11369         if(!this.elements[index]){
11370             return null;
11371         }
11372         this.el.dom = this.elements[index];
11373         return this.el;
11374     },
11375
11376     // fixes scope with flyweight
11377     addListener : function(eventName, handler, scope, opt){
11378         var els = this.elements;
11379         for(var i = 0, len = els.length; i < len; i++) {
11380             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11381         }
11382         return this;
11383     },
11384
11385     /**
11386     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11387     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11388     * a reference to the dom node, use el.dom.</b>
11389     * @param {Function} fn The function to call
11390     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11391     * @return {CompositeElement} this
11392     */
11393     each : function(fn, scope){
11394         var els = this.elements;
11395         var el = this.el;
11396         for(var i = 0, len = els.length; i < len; i++){
11397             el.dom = els[i];
11398                 if(fn.call(scope || el, el, this, i) === false){
11399                 break;
11400             }
11401         }
11402         return this;
11403     },
11404
11405     indexOf : function(el){
11406         return this.elements.indexOf(Roo.getDom(el));
11407     },
11408
11409     replaceElement : function(el, replacement, domReplace){
11410         var index = typeof el == 'number' ? el : this.indexOf(el);
11411         if(index !== -1){
11412             replacement = Roo.getDom(replacement);
11413             if(domReplace){
11414                 var d = this.elements[index];
11415                 d.parentNode.insertBefore(replacement, d);
11416                 d.parentNode.removeChild(d);
11417             }
11418             this.elements.splice(index, 1, replacement);
11419         }
11420         return this;
11421     }
11422 });
11423 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11424
11425 /*
11426  * Based on:
11427  * Ext JS Library 1.1.1
11428  * Copyright(c) 2006-2007, Ext JS, LLC.
11429  *
11430  * Originally Released Under LGPL - original licence link has changed is not relivant.
11431  *
11432  * Fork - LGPL
11433  * <script type="text/javascript">
11434  */
11435
11436  
11437
11438 /**
11439  * @class Roo.data.Connection
11440  * @extends Roo.util.Observable
11441  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11442  * either to a configured URL, or to a URL specified at request time.<br><br>
11443  * <p>
11444  * Requests made by this class are asynchronous, and will return immediately. No data from
11445  * the server will be available to the statement immediately following the {@link #request} call.
11446  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11447  * <p>
11448  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11449  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11450  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11451  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11452  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11453  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11454  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11455  * standard DOM methods.
11456  * @constructor
11457  * @param {Object} config a configuration object.
11458  */
11459 Roo.data.Connection = function(config){
11460     Roo.apply(this, config);
11461     this.addEvents({
11462         /**
11463          * @event beforerequest
11464          * Fires before a network request is made to retrieve a data object.
11465          * @param {Connection} conn This Connection object.
11466          * @param {Object} options The options config object passed to the {@link #request} method.
11467          */
11468         "beforerequest" : true,
11469         /**
11470          * @event requestcomplete
11471          * Fires if the request was successfully completed.
11472          * @param {Connection} conn This Connection object.
11473          * @param {Object} response The XHR object containing the response data.
11474          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11475          * @param {Object} options The options config object passed to the {@link #request} method.
11476          */
11477         "requestcomplete" : true,
11478         /**
11479          * @event requestexception
11480          * Fires if an error HTTP status was returned from the server.
11481          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11482          * @param {Connection} conn This Connection object.
11483          * @param {Object} response The XHR object containing the response data.
11484          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11485          * @param {Object} options The options config object passed to the {@link #request} method.
11486          */
11487         "requestexception" : true
11488     });
11489     Roo.data.Connection.superclass.constructor.call(this);
11490 };
11491
11492 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11493     /**
11494      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11495      */
11496     /**
11497      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11498      * extra parameters to each request made by this object. (defaults to undefined)
11499      */
11500     /**
11501      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11502      *  to each request made by this object. (defaults to undefined)
11503      */
11504     /**
11505      * @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)
11506      */
11507     /**
11508      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11509      */
11510     timeout : 30000,
11511     /**
11512      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11513      * @type Boolean
11514      */
11515     autoAbort:false,
11516
11517     /**
11518      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11519      * @type Boolean
11520      */
11521     disableCaching: true,
11522
11523     /**
11524      * Sends an HTTP request to a remote server.
11525      * @param {Object} options An object which may contain the following properties:<ul>
11526      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11527      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11528      * request, a url encoded string or a function to call to get either.</li>
11529      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11530      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11531      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11532      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11533      * <li>options {Object} The parameter to the request call.</li>
11534      * <li>success {Boolean} True if the request succeeded.</li>
11535      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11536      * </ul></li>
11537      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11538      * The callback is passed the following parameters:<ul>
11539      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11540      * <li>options {Object} The parameter to the request call.</li>
11541      * </ul></li>
11542      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11543      * The callback is passed the following parameters:<ul>
11544      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11545      * <li>options {Object} The parameter to the request call.</li>
11546      * </ul></li>
11547      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11548      * for the callback function. Defaults to the browser window.</li>
11549      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11550      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11551      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11552      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11553      * params for the post data. Any params will be appended to the URL.</li>
11554      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11555      * </ul>
11556      * @return {Number} transactionId
11557      */
11558     request : function(o){
11559         if(this.fireEvent("beforerequest", this, o) !== false){
11560             var p = o.params;
11561
11562             if(typeof p == "function"){
11563                 p = p.call(o.scope||window, o);
11564             }
11565             if(typeof p == "object"){
11566                 p = Roo.urlEncode(o.params);
11567             }
11568             if(this.extraParams){
11569                 var extras = Roo.urlEncode(this.extraParams);
11570                 p = p ? (p + '&' + extras) : extras;
11571             }
11572
11573             var url = o.url || this.url;
11574             if(typeof url == 'function'){
11575                 url = url.call(o.scope||window, o);
11576             }
11577
11578             if(o.form){
11579                 var form = Roo.getDom(o.form);
11580                 url = url || form.action;
11581
11582                 var enctype = form.getAttribute("enctype");
11583                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11584                     return this.doFormUpload(o, p, url);
11585                 }
11586                 var f = Roo.lib.Ajax.serializeForm(form);
11587                 p = p ? (p + '&' + f) : f;
11588             }
11589
11590             var hs = o.headers;
11591             if(this.defaultHeaders){
11592                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11593                 if(!o.headers){
11594                     o.headers = hs;
11595                 }
11596             }
11597
11598             var cb = {
11599                 success: this.handleResponse,
11600                 failure: this.handleFailure,
11601                 scope: this,
11602                 argument: {options: o},
11603                 timeout : o.timeout || this.timeout
11604             };
11605
11606             var method = o.method||this.method||(p ? "POST" : "GET");
11607
11608             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11609                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11610             }
11611
11612             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11613                 if(o.autoAbort){
11614                     this.abort();
11615                 }
11616             }else if(this.autoAbort !== false){
11617                 this.abort();
11618             }
11619
11620             if((method == 'GET' && p) || o.xmlData){
11621                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11622                 p = '';
11623             }
11624             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11625             return this.transId;
11626         }else{
11627             Roo.callback(o.callback, o.scope, [o, null, null]);
11628             return null;
11629         }
11630     },
11631
11632     /**
11633      * Determine whether this object has a request outstanding.
11634      * @param {Number} transactionId (Optional) defaults to the last transaction
11635      * @return {Boolean} True if there is an outstanding request.
11636      */
11637     isLoading : function(transId){
11638         if(transId){
11639             return Roo.lib.Ajax.isCallInProgress(transId);
11640         }else{
11641             return this.transId ? true : false;
11642         }
11643     },
11644
11645     /**
11646      * Aborts any outstanding request.
11647      * @param {Number} transactionId (Optional) defaults to the last transaction
11648      */
11649     abort : function(transId){
11650         if(transId || this.isLoading()){
11651             Roo.lib.Ajax.abort(transId || this.transId);
11652         }
11653     },
11654
11655     // private
11656     handleResponse : function(response){
11657         this.transId = false;
11658         var options = response.argument.options;
11659         response.argument = options ? options.argument : null;
11660         this.fireEvent("requestcomplete", this, response, options);
11661         Roo.callback(options.success, options.scope, [response, options]);
11662         Roo.callback(options.callback, options.scope, [options, true, response]);
11663     },
11664
11665     // private
11666     handleFailure : function(response, e){
11667         this.transId = false;
11668         var options = response.argument.options;
11669         response.argument = options ? options.argument : null;
11670         this.fireEvent("requestexception", this, response, options, e);
11671         Roo.callback(options.failure, options.scope, [response, options]);
11672         Roo.callback(options.callback, options.scope, [options, false, response]);
11673     },
11674
11675     // private
11676     doFormUpload : function(o, ps, url){
11677         var id = Roo.id();
11678         var frame = document.createElement('iframe');
11679         frame.id = id;
11680         frame.name = id;
11681         frame.className = 'x-hidden';
11682         if(Roo.isIE){
11683             frame.src = Roo.SSL_SECURE_URL;
11684         }
11685         document.body.appendChild(frame);
11686
11687         if(Roo.isIE){
11688            document.frames[id].name = id;
11689         }
11690
11691         var form = Roo.getDom(o.form);
11692         form.target = id;
11693         form.method = 'POST';
11694         form.enctype = form.encoding = 'multipart/form-data';
11695         if(url){
11696             form.action = url;
11697         }
11698
11699         var hiddens, hd;
11700         if(ps){ // add dynamic params
11701             hiddens = [];
11702             ps = Roo.urlDecode(ps, false);
11703             for(var k in ps){
11704                 if(ps.hasOwnProperty(k)){
11705                     hd = document.createElement('input');
11706                     hd.type = 'hidden';
11707                     hd.name = k;
11708                     hd.value = ps[k];
11709                     form.appendChild(hd);
11710                     hiddens.push(hd);
11711                 }
11712             }
11713         }
11714
11715         function cb(){
11716             var r = {  // bogus response object
11717                 responseText : '',
11718                 responseXML : null
11719             };
11720
11721             r.argument = o ? o.argument : null;
11722
11723             try { //
11724                 var doc;
11725                 if(Roo.isIE){
11726                     doc = frame.contentWindow.document;
11727                 }else {
11728                     doc = (frame.contentDocument || window.frames[id].document);
11729                 }
11730                 if(doc && doc.body){
11731                     r.responseText = doc.body.innerHTML;
11732                 }
11733                 if(doc && doc.XMLDocument){
11734                     r.responseXML = doc.XMLDocument;
11735                 }else {
11736                     r.responseXML = doc;
11737                 }
11738             }
11739             catch(e) {
11740                 // ignore
11741             }
11742
11743             Roo.EventManager.removeListener(frame, 'load', cb, this);
11744
11745             this.fireEvent("requestcomplete", this, r, o);
11746             Roo.callback(o.success, o.scope, [r, o]);
11747             Roo.callback(o.callback, o.scope, [o, true, r]);
11748
11749             setTimeout(function(){document.body.removeChild(frame);}, 100);
11750         }
11751
11752         Roo.EventManager.on(frame, 'load', cb, this);
11753         form.submit();
11754
11755         if(hiddens){ // remove dynamic params
11756             for(var i = 0, len = hiddens.length; i < len; i++){
11757                 form.removeChild(hiddens[i]);
11758             }
11759         }
11760     }
11761 });
11762 /*
11763  * Based on:
11764  * Ext JS Library 1.1.1
11765  * Copyright(c) 2006-2007, Ext JS, LLC.
11766  *
11767  * Originally Released Under LGPL - original licence link has changed is not relivant.
11768  *
11769  * Fork - LGPL
11770  * <script type="text/javascript">
11771  */
11772  
11773 /**
11774  * Global Ajax request class.
11775  * 
11776  * @class Roo.Ajax
11777  * @extends Roo.data.Connection
11778  * @static
11779  * 
11780  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11781  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11782  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11783  * @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)
11784  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11785  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11786  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11787  */
11788 Roo.Ajax = new Roo.data.Connection({
11789     // fix up the docs
11790     /**
11791      * @scope Roo.Ajax
11792      * @type {Boolear} 
11793      */
11794     autoAbort : false,
11795
11796     /**
11797      * Serialize the passed form into a url encoded string
11798      * @scope Roo.Ajax
11799      * @param {String/HTMLElement} form
11800      * @return {String}
11801      */
11802     serializeForm : function(form){
11803         return Roo.lib.Ajax.serializeForm(form);
11804     }
11805 });/*
11806  * Based on:
11807  * Ext JS Library 1.1.1
11808  * Copyright(c) 2006-2007, Ext JS, LLC.
11809  *
11810  * Originally Released Under LGPL - original licence link has changed is not relivant.
11811  *
11812  * Fork - LGPL
11813  * <script type="text/javascript">
11814  */
11815
11816  
11817 /**
11818  * @class Roo.UpdateManager
11819  * @extends Roo.util.Observable
11820  * Provides AJAX-style update for Element object.<br><br>
11821  * Usage:<br>
11822  * <pre><code>
11823  * // Get it from a Roo.Element object
11824  * var el = Roo.get("foo");
11825  * var mgr = el.getUpdateManager();
11826  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11827  * ...
11828  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11829  * <br>
11830  * // or directly (returns the same UpdateManager instance)
11831  * var mgr = new Roo.UpdateManager("myElementId");
11832  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11833  * mgr.on("update", myFcnNeedsToKnow);
11834  * <br>
11835    // short handed call directly from the element object
11836    Roo.get("foo").load({
11837         url: "bar.php",
11838         scripts:true,
11839         params: "for=bar",
11840         text: "Loading Foo..."
11841    });
11842  * </code></pre>
11843  * @constructor
11844  * Create new UpdateManager directly.
11845  * @param {String/HTMLElement/Roo.Element} el The element to update
11846  * @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).
11847  */
11848 Roo.UpdateManager = function(el, forceNew){
11849     el = Roo.get(el);
11850     if(!forceNew && el.updateManager){
11851         return el.updateManager;
11852     }
11853     /**
11854      * The Element object
11855      * @type Roo.Element
11856      */
11857     this.el = el;
11858     /**
11859      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11860      * @type String
11861      */
11862     this.defaultUrl = null;
11863
11864     this.addEvents({
11865         /**
11866          * @event beforeupdate
11867          * Fired before an update is made, return false from your handler and the update is cancelled.
11868          * @param {Roo.Element} el
11869          * @param {String/Object/Function} url
11870          * @param {String/Object} params
11871          */
11872         "beforeupdate": true,
11873         /**
11874          * @event update
11875          * Fired after successful update is made.
11876          * @param {Roo.Element} el
11877          * @param {Object} oResponseObject The response Object
11878          */
11879         "update": true,
11880         /**
11881          * @event failure
11882          * Fired on update failure.
11883          * @param {Roo.Element} el
11884          * @param {Object} oResponseObject The response Object
11885          */
11886         "failure": true
11887     });
11888     var d = Roo.UpdateManager.defaults;
11889     /**
11890      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11891      * @type String
11892      */
11893     this.sslBlankUrl = d.sslBlankUrl;
11894     /**
11895      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11896      * @type Boolean
11897      */
11898     this.disableCaching = d.disableCaching;
11899     /**
11900      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11901      * @type String
11902      */
11903     this.indicatorText = d.indicatorText;
11904     /**
11905      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11906      * @type String
11907      */
11908     this.showLoadIndicator = d.showLoadIndicator;
11909     /**
11910      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11911      * @type Number
11912      */
11913     this.timeout = d.timeout;
11914
11915     /**
11916      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11917      * @type Boolean
11918      */
11919     this.loadScripts = d.loadScripts;
11920
11921     /**
11922      * Transaction object of current executing transaction
11923      */
11924     this.transaction = null;
11925
11926     /**
11927      * @private
11928      */
11929     this.autoRefreshProcId = null;
11930     /**
11931      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11932      * @type Function
11933      */
11934     this.refreshDelegate = this.refresh.createDelegate(this);
11935     /**
11936      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11937      * @type Function
11938      */
11939     this.updateDelegate = this.update.createDelegate(this);
11940     /**
11941      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11942      * @type Function
11943      */
11944     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11945     /**
11946      * @private
11947      */
11948     this.successDelegate = this.processSuccess.createDelegate(this);
11949     /**
11950      * @private
11951      */
11952     this.failureDelegate = this.processFailure.createDelegate(this);
11953
11954     if(!this.renderer){
11955      /**
11956       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11957       */
11958     this.renderer = new Roo.UpdateManager.BasicRenderer();
11959     }
11960     
11961     Roo.UpdateManager.superclass.constructor.call(this);
11962 };
11963
11964 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11965     /**
11966      * Get the Element this UpdateManager is bound to
11967      * @return {Roo.Element} The element
11968      */
11969     getEl : function(){
11970         return this.el;
11971     },
11972     /**
11973      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11974      * @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:
11975 <pre><code>
11976 um.update({<br/>
11977     url: "your-url.php",<br/>
11978     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11979     callback: yourFunction,<br/>
11980     scope: yourObject, //(optional scope)  <br/>
11981     discardUrl: false, <br/>
11982     nocache: false,<br/>
11983     text: "Loading...",<br/>
11984     timeout: 30,<br/>
11985     scripts: false<br/>
11986 });
11987 </code></pre>
11988      * The only required property is url. The optional properties nocache, text and scripts
11989      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11990      * @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}
11991      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11992      * @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.
11993      */
11994     update : function(url, params, callback, discardUrl){
11995         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11996             var method = this.method,
11997                 cfg;
11998             if(typeof url == "object"){ // must be config object
11999                 cfg = url;
12000                 url = cfg.url;
12001                 params = params || cfg.params;
12002                 callback = callback || cfg.callback;
12003                 discardUrl = discardUrl || cfg.discardUrl;
12004                 if(callback && cfg.scope){
12005                     callback = callback.createDelegate(cfg.scope);
12006                 }
12007                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12008                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12009                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12010                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12011                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12012             }
12013             this.showLoading();
12014             if(!discardUrl){
12015                 this.defaultUrl = url;
12016             }
12017             if(typeof url == "function"){
12018                 url = url.call(this);
12019             }
12020
12021             method = method || (params ? "POST" : "GET");
12022             if(method == "GET"){
12023                 url = this.prepareUrl(url);
12024             }
12025
12026             var o = Roo.apply(cfg ||{}, {
12027                 url : url,
12028                 params: params,
12029                 success: this.successDelegate,
12030                 failure: this.failureDelegate,
12031                 callback: undefined,
12032                 timeout: (this.timeout*1000),
12033                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12034             });
12035             Roo.log("updated manager called with timeout of " + o.timeout);
12036             this.transaction = Roo.Ajax.request(o);
12037         }
12038     },
12039
12040     /**
12041      * 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.
12042      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12043      * @param {String/HTMLElement} form The form Id or form element
12044      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12045      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12046      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12047      */
12048     formUpdate : function(form, url, reset, callback){
12049         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12050             if(typeof url == "function"){
12051                 url = url.call(this);
12052             }
12053             form = Roo.getDom(form);
12054             this.transaction = Roo.Ajax.request({
12055                 form: form,
12056                 url:url,
12057                 success: this.successDelegate,
12058                 failure: this.failureDelegate,
12059                 timeout: (this.timeout*1000),
12060                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12061             });
12062             this.showLoading.defer(1, this);
12063         }
12064     },
12065
12066     /**
12067      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12068      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12069      */
12070     refresh : function(callback){
12071         if(this.defaultUrl == null){
12072             return;
12073         }
12074         this.update(this.defaultUrl, null, callback, true);
12075     },
12076
12077     /**
12078      * Set this element to auto refresh.
12079      * @param {Number} interval How often to update (in seconds).
12080      * @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)
12081      * @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}
12082      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12083      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12084      */
12085     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12086         if(refreshNow){
12087             this.update(url || this.defaultUrl, params, callback, true);
12088         }
12089         if(this.autoRefreshProcId){
12090             clearInterval(this.autoRefreshProcId);
12091         }
12092         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12093     },
12094
12095     /**
12096      * Stop auto refresh on this element.
12097      */
12098      stopAutoRefresh : function(){
12099         if(this.autoRefreshProcId){
12100             clearInterval(this.autoRefreshProcId);
12101             delete this.autoRefreshProcId;
12102         }
12103     },
12104
12105     isAutoRefreshing : function(){
12106        return this.autoRefreshProcId ? true : false;
12107     },
12108     /**
12109      * Called to update the element to "Loading" state. Override to perform custom action.
12110      */
12111     showLoading : function(){
12112         if(this.showLoadIndicator){
12113             this.el.update(this.indicatorText);
12114         }
12115     },
12116
12117     /**
12118      * Adds unique parameter to query string if disableCaching = true
12119      * @private
12120      */
12121     prepareUrl : function(url){
12122         if(this.disableCaching){
12123             var append = "_dc=" + (new Date().getTime());
12124             if(url.indexOf("?") !== -1){
12125                 url += "&" + append;
12126             }else{
12127                 url += "?" + append;
12128             }
12129         }
12130         return url;
12131     },
12132
12133     /**
12134      * @private
12135      */
12136     processSuccess : function(response){
12137         this.transaction = null;
12138         if(response.argument.form && response.argument.reset){
12139             try{ // put in try/catch since some older FF releases had problems with this
12140                 response.argument.form.reset();
12141             }catch(e){}
12142         }
12143         if(this.loadScripts){
12144             this.renderer.render(this.el, response, this,
12145                 this.updateComplete.createDelegate(this, [response]));
12146         }else{
12147             this.renderer.render(this.el, response, this);
12148             this.updateComplete(response);
12149         }
12150     },
12151
12152     updateComplete : function(response){
12153         this.fireEvent("update", this.el, response);
12154         if(typeof response.argument.callback == "function"){
12155             response.argument.callback(this.el, true, response);
12156         }
12157     },
12158
12159     /**
12160      * @private
12161      */
12162     processFailure : function(response){
12163         this.transaction = null;
12164         this.fireEvent("failure", this.el, response);
12165         if(typeof response.argument.callback == "function"){
12166             response.argument.callback(this.el, false, response);
12167         }
12168     },
12169
12170     /**
12171      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12172      * @param {Object} renderer The object implementing the render() method
12173      */
12174     setRenderer : function(renderer){
12175         this.renderer = renderer;
12176     },
12177
12178     getRenderer : function(){
12179        return this.renderer;
12180     },
12181
12182     /**
12183      * Set the defaultUrl used for updates
12184      * @param {String/Function} defaultUrl The url or a function to call to get the url
12185      */
12186     setDefaultUrl : function(defaultUrl){
12187         this.defaultUrl = defaultUrl;
12188     },
12189
12190     /**
12191      * Aborts the executing transaction
12192      */
12193     abort : function(){
12194         if(this.transaction){
12195             Roo.Ajax.abort(this.transaction);
12196         }
12197     },
12198
12199     /**
12200      * Returns true if an update is in progress
12201      * @return {Boolean}
12202      */
12203     isUpdating : function(){
12204         if(this.transaction){
12205             return Roo.Ajax.isLoading(this.transaction);
12206         }
12207         return false;
12208     }
12209 });
12210
12211 /**
12212  * @class Roo.UpdateManager.defaults
12213  * @static (not really - but it helps the doc tool)
12214  * The defaults collection enables customizing the default properties of UpdateManager
12215  */
12216    Roo.UpdateManager.defaults = {
12217        /**
12218          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12219          * @type Number
12220          */
12221          timeout : 30,
12222
12223          /**
12224          * True to process scripts by default (Defaults to false).
12225          * @type Boolean
12226          */
12227         loadScripts : false,
12228
12229         /**
12230         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12231         * @type String
12232         */
12233         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12234         /**
12235          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12236          * @type Boolean
12237          */
12238         disableCaching : false,
12239         /**
12240          * Whether to show indicatorText when loading (Defaults to true).
12241          * @type Boolean
12242          */
12243         showLoadIndicator : true,
12244         /**
12245          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12246          * @type String
12247          */
12248         indicatorText : '<div class="loading-indicator">Loading...</div>'
12249    };
12250
12251 /**
12252  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12253  *Usage:
12254  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12255  * @param {String/HTMLElement/Roo.Element} el The element to update
12256  * @param {String} url The url
12257  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12258  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12259  * @static
12260  * @deprecated
12261  * @member Roo.UpdateManager
12262  */
12263 Roo.UpdateManager.updateElement = function(el, url, params, options){
12264     var um = Roo.get(el, true).getUpdateManager();
12265     Roo.apply(um, options);
12266     um.update(url, params, options ? options.callback : null);
12267 };
12268 // alias for backwards compat
12269 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12270 /**
12271  * @class Roo.UpdateManager.BasicRenderer
12272  * Default Content renderer. Updates the elements innerHTML with the responseText.
12273  */
12274 Roo.UpdateManager.BasicRenderer = function(){};
12275
12276 Roo.UpdateManager.BasicRenderer.prototype = {
12277     /**
12278      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12279      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12280      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12281      * @param {Roo.Element} el The element being rendered
12282      * @param {Object} response The YUI Connect response object
12283      * @param {UpdateManager} updateManager The calling update manager
12284      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12285      */
12286      render : function(el, response, updateManager, callback){
12287         el.update(response.responseText, updateManager.loadScripts, callback);
12288     }
12289 };
12290 /*
12291  * Based on:
12292  * Roo JS
12293  * (c)) Alan Knowles
12294  * Licence : LGPL
12295  */
12296
12297
12298 /**
12299  * @class Roo.DomTemplate
12300  * @extends Roo.Template
12301  * An effort at a dom based template engine..
12302  *
12303  * Similar to XTemplate, except it uses dom parsing to create the template..
12304  *
12305  * Supported features:
12306  *
12307  *  Tags:
12308
12309 <pre><code>
12310       {a_variable} - output encoded.
12311       {a_variable.format:("Y-m-d")} - call a method on the variable
12312       {a_variable:raw} - unencoded output
12313       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12314       {a_variable:this.method_on_template(...)} - call a method on the template object.
12315  
12316 </code></pre>
12317  *  The tpl tag:
12318 <pre><code>
12319         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12320         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12321         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12322         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12323   
12324 </code></pre>
12325  *      
12326  */
12327 Roo.DomTemplate = function()
12328 {
12329      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12330      if (this.html) {
12331         this.compile();
12332      }
12333 };
12334
12335
12336 Roo.extend(Roo.DomTemplate, Roo.Template, {
12337     /**
12338      * id counter for sub templates.
12339      */
12340     id : 0,
12341     /**
12342      * flag to indicate if dom parser is inside a pre,
12343      * it will strip whitespace if not.
12344      */
12345     inPre : false,
12346     
12347     /**
12348      * The various sub templates
12349      */
12350     tpls : false,
12351     
12352     
12353     
12354     /**
12355      *
12356      * basic tag replacing syntax
12357      * WORD:WORD()
12358      *
12359      * // you can fake an object call by doing this
12360      *  x.t:(test,tesT) 
12361      * 
12362      */
12363     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12364     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12365     
12366     iterChild : function (node, method) {
12367         
12368         var oldPre = this.inPre;
12369         if (node.tagName == 'PRE') {
12370             this.inPre = true;
12371         }
12372         for( var i = 0; i < node.childNodes.length; i++) {
12373             method.call(this, node.childNodes[i]);
12374         }
12375         this.inPre = oldPre;
12376     },
12377     
12378     
12379     
12380     /**
12381      * compile the template
12382      *
12383      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12384      *
12385      */
12386     compile: function()
12387     {
12388         var s = this.html;
12389         
12390         // covert the html into DOM...
12391         var doc = false;
12392         var div =false;
12393         try {
12394             doc = document.implementation.createHTMLDocument("");
12395             doc.documentElement.innerHTML =   this.html  ;
12396             div = doc.documentElement;
12397         } catch (e) {
12398             // old IE... - nasty -- it causes all sorts of issues.. with
12399             // images getting pulled from server..
12400             div = document.createElement('div');
12401             div.innerHTML = this.html;
12402         }
12403         //doc.documentElement.innerHTML = htmlBody
12404          
12405         
12406         
12407         this.tpls = [];
12408         var _t = this;
12409         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12410         
12411         var tpls = this.tpls;
12412         
12413         // create a top level template from the snippet..
12414         
12415         //Roo.log(div.innerHTML);
12416         
12417         var tpl = {
12418             uid : 'master',
12419             id : this.id++,
12420             attr : false,
12421             value : false,
12422             body : div.innerHTML,
12423             
12424             forCall : false,
12425             execCall : false,
12426             dom : div,
12427             isTop : true
12428             
12429         };
12430         tpls.unshift(tpl);
12431         
12432         
12433         // compile them...
12434         this.tpls = [];
12435         Roo.each(tpls, function(tp){
12436             this.compileTpl(tp);
12437             this.tpls[tp.id] = tp;
12438         }, this);
12439         
12440         this.master = tpls[0];
12441         return this;
12442         
12443         
12444     },
12445     
12446     compileNode : function(node, istop) {
12447         // test for
12448         //Roo.log(node);
12449         
12450         
12451         // skip anything not a tag..
12452         if (node.nodeType != 1) {
12453             if (node.nodeType == 3 && !this.inPre) {
12454                 // reduce white space..
12455                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12456                 
12457             }
12458             return;
12459         }
12460         
12461         var tpl = {
12462             uid : false,
12463             id : false,
12464             attr : false,
12465             value : false,
12466             body : '',
12467             
12468             forCall : false,
12469             execCall : false,
12470             dom : false,
12471             isTop : istop
12472             
12473             
12474         };
12475         
12476         
12477         switch(true) {
12478             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12479             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12480             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12481             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12482             // no default..
12483         }
12484         
12485         
12486         if (!tpl.attr) {
12487             // just itterate children..
12488             this.iterChild(node,this.compileNode);
12489             return;
12490         }
12491         tpl.uid = this.id++;
12492         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12493         node.removeAttribute('roo-'+ tpl.attr);
12494         if (tpl.attr != 'name') {
12495             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12496             node.parentNode.replaceChild(placeholder,  node);
12497         } else {
12498             
12499             var placeholder =  document.createElement('span');
12500             placeholder.className = 'roo-tpl-' + tpl.value;
12501             node.parentNode.replaceChild(placeholder,  node);
12502         }
12503         
12504         // parent now sees '{domtplXXXX}
12505         this.iterChild(node,this.compileNode);
12506         
12507         // we should now have node body...
12508         var div = document.createElement('div');
12509         div.appendChild(node);
12510         tpl.dom = node;
12511         // this has the unfortunate side effect of converting tagged attributes
12512         // eg. href="{...}" into %7C...%7D
12513         // this has been fixed by searching for those combo's although it's a bit hacky..
12514         
12515         
12516         tpl.body = div.innerHTML;
12517         
12518         
12519          
12520         tpl.id = tpl.uid;
12521         switch(tpl.attr) {
12522             case 'for' :
12523                 switch (tpl.value) {
12524                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12525                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12526                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12527                 }
12528                 break;
12529             
12530             case 'exec':
12531                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12532                 break;
12533             
12534             case 'if':     
12535                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12536                 break;
12537             
12538             case 'name':
12539                 tpl.id  = tpl.value; // replace non characters???
12540                 break;
12541             
12542         }
12543         
12544         
12545         this.tpls.push(tpl);
12546         
12547         
12548         
12549     },
12550     
12551     
12552     
12553     
12554     /**
12555      * Compile a segment of the template into a 'sub-template'
12556      *
12557      * 
12558      * 
12559      *
12560      */
12561     compileTpl : function(tpl)
12562     {
12563         var fm = Roo.util.Format;
12564         var useF = this.disableFormats !== true;
12565         
12566         var sep = Roo.isGecko ? "+\n" : ",\n";
12567         
12568         var undef = function(str) {
12569             Roo.debug && Roo.log("Property not found :"  + str);
12570             return '';
12571         };
12572           
12573         //Roo.log(tpl.body);
12574         
12575         
12576         
12577         var fn = function(m, lbrace, name, format, args)
12578         {
12579             //Roo.log("ARGS");
12580             //Roo.log(arguments);
12581             args = args ? args.replace(/\\'/g,"'") : args;
12582             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12583             if (typeof(format) == 'undefined') {
12584                 format =  'htmlEncode'; 
12585             }
12586             if (format == 'raw' ) {
12587                 format = false;
12588             }
12589             
12590             if(name.substr(0, 6) == 'domtpl'){
12591                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12592             }
12593             
12594             // build an array of options to determine if value is undefined..
12595             
12596             // basically get 'xxxx.yyyy' then do
12597             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12598             //    (function () { Roo.log("Property not found"); return ''; })() :
12599             //    ......
12600             
12601             var udef_ar = [];
12602             var lookfor = '';
12603             Roo.each(name.split('.'), function(st) {
12604                 lookfor += (lookfor.length ? '.': '') + st;
12605                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12606             });
12607             
12608             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12609             
12610             
12611             if(format && useF){
12612                 
12613                 args = args ? ',' + args : "";
12614                  
12615                 if(format.substr(0, 5) != "this."){
12616                     format = "fm." + format + '(';
12617                 }else{
12618                     format = 'this.call("'+ format.substr(5) + '", ';
12619                     args = ", values";
12620                 }
12621                 
12622                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12623             }
12624              
12625             if (args && args.length) {
12626                 // called with xxyx.yuu:(test,test)
12627                 // change to ()
12628                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12629             }
12630             // raw.. - :raw modifier..
12631             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12632             
12633         };
12634         var body;
12635         // branched to use + in gecko and [].join() in others
12636         if(Roo.isGecko){
12637             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12638                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12639                     "';};};";
12640         }else{
12641             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12642             body.push(tpl.body.replace(/(\r\n|\n)/g,
12643                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12644             body.push("'].join('');};};");
12645             body = body.join('');
12646         }
12647         
12648         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12649        
12650         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12651         eval(body);
12652         
12653         return this;
12654     },
12655      
12656     /**
12657      * same as applyTemplate, except it's done to one of the subTemplates
12658      * when using named templates, you can do:
12659      *
12660      * var str = pl.applySubTemplate('your-name', values);
12661      *
12662      * 
12663      * @param {Number} id of the template
12664      * @param {Object} values to apply to template
12665      * @param {Object} parent (normaly the instance of this object)
12666      */
12667     applySubTemplate : function(id, values, parent)
12668     {
12669         
12670         
12671         var t = this.tpls[id];
12672         
12673         
12674         try { 
12675             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12676                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12677                 return '';
12678             }
12679         } catch(e) {
12680             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12681             Roo.log(values);
12682           
12683             return '';
12684         }
12685         try { 
12686             
12687             if(t.execCall && t.execCall.call(this, values, parent)){
12688                 return '';
12689             }
12690         } catch(e) {
12691             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12692             Roo.log(values);
12693             return '';
12694         }
12695         
12696         try {
12697             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12698             parent = t.target ? values : parent;
12699             if(t.forCall && vs instanceof Array){
12700                 var buf = [];
12701                 for(var i = 0, len = vs.length; i < len; i++){
12702                     try {
12703                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12704                     } catch (e) {
12705                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12706                         Roo.log(e.body);
12707                         //Roo.log(t.compiled);
12708                         Roo.log(vs[i]);
12709                     }   
12710                 }
12711                 return buf.join('');
12712             }
12713         } catch (e) {
12714             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12715             Roo.log(values);
12716             return '';
12717         }
12718         try {
12719             return t.compiled.call(this, vs, parent);
12720         } catch (e) {
12721             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12722             Roo.log(e.body);
12723             //Roo.log(t.compiled);
12724             Roo.log(values);
12725             return '';
12726         }
12727     },
12728
12729    
12730
12731     applyTemplate : function(values){
12732         return this.master.compiled.call(this, values, {});
12733         //var s = this.subs;
12734     },
12735
12736     apply : function(){
12737         return this.applyTemplate.apply(this, arguments);
12738     }
12739
12740  });
12741
12742 Roo.DomTemplate.from = function(el){
12743     el = Roo.getDom(el);
12744     return new Roo.Domtemplate(el.value || el.innerHTML);
12745 };/*
12746  * Based on:
12747  * Ext JS Library 1.1.1
12748  * Copyright(c) 2006-2007, Ext JS, LLC.
12749  *
12750  * Originally Released Under LGPL - original licence link has changed is not relivant.
12751  *
12752  * Fork - LGPL
12753  * <script type="text/javascript">
12754  */
12755
12756 /**
12757  * @class Roo.util.DelayedTask
12758  * Provides a convenient method of performing setTimeout where a new
12759  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12760  * You can use this class to buffer
12761  * the keypress events for a certain number of milliseconds, and perform only if they stop
12762  * for that amount of time.
12763  * @constructor The parameters to this constructor serve as defaults and are not required.
12764  * @param {Function} fn (optional) The default function to timeout
12765  * @param {Object} scope (optional) The default scope of that timeout
12766  * @param {Array} args (optional) The default Array of arguments
12767  */
12768 Roo.util.DelayedTask = function(fn, scope, args){
12769     var id = null, d, t;
12770
12771     var call = function(){
12772         var now = new Date().getTime();
12773         if(now - t >= d){
12774             clearInterval(id);
12775             id = null;
12776             fn.apply(scope, args || []);
12777         }
12778     };
12779     /**
12780      * Cancels any pending timeout and queues a new one
12781      * @param {Number} delay The milliseconds to delay
12782      * @param {Function} newFn (optional) Overrides function passed to constructor
12783      * @param {Object} newScope (optional) Overrides scope passed to constructor
12784      * @param {Array} newArgs (optional) Overrides args passed to constructor
12785      */
12786     this.delay = function(delay, newFn, newScope, newArgs){
12787         if(id && delay != d){
12788             this.cancel();
12789         }
12790         d = delay;
12791         t = new Date().getTime();
12792         fn = newFn || fn;
12793         scope = newScope || scope;
12794         args = newArgs || args;
12795         if(!id){
12796             id = setInterval(call, d);
12797         }
12798     };
12799
12800     /**
12801      * Cancel the last queued timeout
12802      */
12803     this.cancel = function(){
12804         if(id){
12805             clearInterval(id);
12806             id = null;
12807         }
12808     };
12809 };/*
12810  * Based on:
12811  * Ext JS Library 1.1.1
12812  * Copyright(c) 2006-2007, Ext JS, LLC.
12813  *
12814  * Originally Released Under LGPL - original licence link has changed is not relivant.
12815  *
12816  * Fork - LGPL
12817  * <script type="text/javascript">
12818  */
12819  
12820  
12821 Roo.util.TaskRunner = function(interval){
12822     interval = interval || 10;
12823     var tasks = [], removeQueue = [];
12824     var id = 0;
12825     var running = false;
12826
12827     var stopThread = function(){
12828         running = false;
12829         clearInterval(id);
12830         id = 0;
12831     };
12832
12833     var startThread = function(){
12834         if(!running){
12835             running = true;
12836             id = setInterval(runTasks, interval);
12837         }
12838     };
12839
12840     var removeTask = function(task){
12841         removeQueue.push(task);
12842         if(task.onStop){
12843             task.onStop();
12844         }
12845     };
12846
12847     var runTasks = function(){
12848         if(removeQueue.length > 0){
12849             for(var i = 0, len = removeQueue.length; i < len; i++){
12850                 tasks.remove(removeQueue[i]);
12851             }
12852             removeQueue = [];
12853             if(tasks.length < 1){
12854                 stopThread();
12855                 return;
12856             }
12857         }
12858         var now = new Date().getTime();
12859         for(var i = 0, len = tasks.length; i < len; ++i){
12860             var t = tasks[i];
12861             var itime = now - t.taskRunTime;
12862             if(t.interval <= itime){
12863                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12864                 t.taskRunTime = now;
12865                 if(rt === false || t.taskRunCount === t.repeat){
12866                     removeTask(t);
12867                     return;
12868                 }
12869             }
12870             if(t.duration && t.duration <= (now - t.taskStartTime)){
12871                 removeTask(t);
12872             }
12873         }
12874     };
12875
12876     /**
12877      * Queues a new task.
12878      * @param {Object} task
12879      */
12880     this.start = function(task){
12881         tasks.push(task);
12882         task.taskStartTime = new Date().getTime();
12883         task.taskRunTime = 0;
12884         task.taskRunCount = 0;
12885         startThread();
12886         return task;
12887     };
12888
12889     this.stop = function(task){
12890         removeTask(task);
12891         return task;
12892     };
12893
12894     this.stopAll = function(){
12895         stopThread();
12896         for(var i = 0, len = tasks.length; i < len; i++){
12897             if(tasks[i].onStop){
12898                 tasks[i].onStop();
12899             }
12900         }
12901         tasks = [];
12902         removeQueue = [];
12903     };
12904 };
12905
12906 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12907  * Based on:
12908  * Ext JS Library 1.1.1
12909  * Copyright(c) 2006-2007, Ext JS, LLC.
12910  *
12911  * Originally Released Under LGPL - original licence link has changed is not relivant.
12912  *
12913  * Fork - LGPL
12914  * <script type="text/javascript">
12915  */
12916
12917  
12918 /**
12919  * @class Roo.util.MixedCollection
12920  * @extends Roo.util.Observable
12921  * A Collection class that maintains both numeric indexes and keys and exposes events.
12922  * @constructor
12923  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12924  * collection (defaults to false)
12925  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12926  * and return the key value for that item.  This is used when available to look up the key on items that
12927  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12928  * equivalent to providing an implementation for the {@link #getKey} method.
12929  */
12930 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12931     this.items = [];
12932     this.map = {};
12933     this.keys = [];
12934     this.length = 0;
12935     this.addEvents({
12936         /**
12937          * @event clear
12938          * Fires when the collection is cleared.
12939          */
12940         "clear" : true,
12941         /**
12942          * @event add
12943          * Fires when an item is added to the collection.
12944          * @param {Number} index The index at which the item was added.
12945          * @param {Object} o The item added.
12946          * @param {String} key The key associated with the added item.
12947          */
12948         "add" : true,
12949         /**
12950          * @event replace
12951          * Fires when an item is replaced in the collection.
12952          * @param {String} key he key associated with the new added.
12953          * @param {Object} old The item being replaced.
12954          * @param {Object} new The new item.
12955          */
12956         "replace" : true,
12957         /**
12958          * @event remove
12959          * Fires when an item is removed from the collection.
12960          * @param {Object} o The item being removed.
12961          * @param {String} key (optional) The key associated with the removed item.
12962          */
12963         "remove" : true,
12964         "sort" : true
12965     });
12966     this.allowFunctions = allowFunctions === true;
12967     if(keyFn){
12968         this.getKey = keyFn;
12969     }
12970     Roo.util.MixedCollection.superclass.constructor.call(this);
12971 };
12972
12973 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12974     allowFunctions : false,
12975     
12976 /**
12977  * Adds an item to the collection.
12978  * @param {String} key The key to associate with the item
12979  * @param {Object} o The item to add.
12980  * @return {Object} The item added.
12981  */
12982     add : function(key, o){
12983         if(arguments.length == 1){
12984             o = arguments[0];
12985             key = this.getKey(o);
12986         }
12987         if(typeof key == "undefined" || key === null){
12988             this.length++;
12989             this.items.push(o);
12990             this.keys.push(null);
12991         }else{
12992             var old = this.map[key];
12993             if(old){
12994                 return this.replace(key, o);
12995             }
12996             this.length++;
12997             this.items.push(o);
12998             this.map[key] = o;
12999             this.keys.push(key);
13000         }
13001         this.fireEvent("add", this.length-1, o, key);
13002         return o;
13003     },
13004        
13005 /**
13006   * MixedCollection has a generic way to fetch keys if you implement getKey.
13007 <pre><code>
13008 // normal way
13009 var mc = new Roo.util.MixedCollection();
13010 mc.add(someEl.dom.id, someEl);
13011 mc.add(otherEl.dom.id, otherEl);
13012 //and so on
13013
13014 // using getKey
13015 var mc = new Roo.util.MixedCollection();
13016 mc.getKey = function(el){
13017    return el.dom.id;
13018 };
13019 mc.add(someEl);
13020 mc.add(otherEl);
13021
13022 // or via the constructor
13023 var mc = new Roo.util.MixedCollection(false, function(el){
13024    return el.dom.id;
13025 });
13026 mc.add(someEl);
13027 mc.add(otherEl);
13028 </code></pre>
13029  * @param o {Object} The item for which to find the key.
13030  * @return {Object} The key for the passed item.
13031  */
13032     getKey : function(o){
13033          return o.id; 
13034     },
13035    
13036 /**
13037  * Replaces an item in the collection.
13038  * @param {String} key The key associated with the item to replace, or the item to replace.
13039  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13040  * @return {Object}  The new item.
13041  */
13042     replace : function(key, o){
13043         if(arguments.length == 1){
13044             o = arguments[0];
13045             key = this.getKey(o);
13046         }
13047         var old = this.item(key);
13048         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13049              return this.add(key, o);
13050         }
13051         var index = this.indexOfKey(key);
13052         this.items[index] = o;
13053         this.map[key] = o;
13054         this.fireEvent("replace", key, old, o);
13055         return o;
13056     },
13057    
13058 /**
13059  * Adds all elements of an Array or an Object to the collection.
13060  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13061  * an Array of values, each of which are added to the collection.
13062  */
13063     addAll : function(objs){
13064         if(arguments.length > 1 || objs instanceof Array){
13065             var args = arguments.length > 1 ? arguments : objs;
13066             for(var i = 0, len = args.length; i < len; i++){
13067                 this.add(args[i]);
13068             }
13069         }else{
13070             for(var key in objs){
13071                 if(this.allowFunctions || typeof objs[key] != "function"){
13072                     this.add(key, objs[key]);
13073                 }
13074             }
13075         }
13076     },
13077    
13078 /**
13079  * Executes the specified function once for every item in the collection, passing each
13080  * item as the first and only parameter. returning false from the function will stop the iteration.
13081  * @param {Function} fn The function to execute for each item.
13082  * @param {Object} scope (optional) The scope in which to execute the function.
13083  */
13084     each : function(fn, scope){
13085         var items = [].concat(this.items); // each safe for removal
13086         for(var i = 0, len = items.length; i < len; i++){
13087             if(fn.call(scope || items[i], items[i], i, len) === false){
13088                 break;
13089             }
13090         }
13091     },
13092    
13093 /**
13094  * Executes the specified function once for every key in the collection, passing each
13095  * key, and its associated item as the first two parameters.
13096  * @param {Function} fn The function to execute for each item.
13097  * @param {Object} scope (optional) The scope in which to execute the function.
13098  */
13099     eachKey : function(fn, scope){
13100         for(var i = 0, len = this.keys.length; i < len; i++){
13101             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13102         }
13103     },
13104    
13105 /**
13106  * Returns the first item in the collection which elicits a true return value from the
13107  * passed selection function.
13108  * @param {Function} fn The selection function to execute for each item.
13109  * @param {Object} scope (optional) The scope in which to execute the function.
13110  * @return {Object} The first item in the collection which returned true from the selection function.
13111  */
13112     find : function(fn, scope){
13113         for(var i = 0, len = this.items.length; i < len; i++){
13114             if(fn.call(scope || window, this.items[i], this.keys[i])){
13115                 return this.items[i];
13116             }
13117         }
13118         return null;
13119     },
13120    
13121 /**
13122  * Inserts an item at the specified index in the collection.
13123  * @param {Number} index The index to insert the item at.
13124  * @param {String} key The key to associate with the new item, or the item itself.
13125  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13126  * @return {Object} The item inserted.
13127  */
13128     insert : function(index, key, o){
13129         if(arguments.length == 2){
13130             o = arguments[1];
13131             key = this.getKey(o);
13132         }
13133         if(index >= this.length){
13134             return this.add(key, o);
13135         }
13136         this.length++;
13137         this.items.splice(index, 0, o);
13138         if(typeof key != "undefined" && key != null){
13139             this.map[key] = o;
13140         }
13141         this.keys.splice(index, 0, key);
13142         this.fireEvent("add", index, o, key);
13143         return o;
13144     },
13145    
13146 /**
13147  * Removed an item from the collection.
13148  * @param {Object} o The item to remove.
13149  * @return {Object} The item removed.
13150  */
13151     remove : function(o){
13152         return this.removeAt(this.indexOf(o));
13153     },
13154    
13155 /**
13156  * Remove an item from a specified index in the collection.
13157  * @param {Number} index The index within the collection of the item to remove.
13158  */
13159     removeAt : function(index){
13160         if(index < this.length && index >= 0){
13161             this.length--;
13162             var o = this.items[index];
13163             this.items.splice(index, 1);
13164             var key = this.keys[index];
13165             if(typeof key != "undefined"){
13166                 delete this.map[key];
13167             }
13168             this.keys.splice(index, 1);
13169             this.fireEvent("remove", o, key);
13170         }
13171     },
13172    
13173 /**
13174  * Removed an item associated with the passed key fom the collection.
13175  * @param {String} key The key of the item to remove.
13176  */
13177     removeKey : function(key){
13178         return this.removeAt(this.indexOfKey(key));
13179     },
13180    
13181 /**
13182  * Returns the number of items in the collection.
13183  * @return {Number} the number of items in the collection.
13184  */
13185     getCount : function(){
13186         return this.length; 
13187     },
13188    
13189 /**
13190  * Returns index within the collection of the passed Object.
13191  * @param {Object} o The item to find the index of.
13192  * @return {Number} index of the item.
13193  */
13194     indexOf : function(o){
13195         if(!this.items.indexOf){
13196             for(var i = 0, len = this.items.length; i < len; i++){
13197                 if(this.items[i] == o) {
13198                     return i;
13199                 }
13200             }
13201             return -1;
13202         }else{
13203             return this.items.indexOf(o);
13204         }
13205     },
13206    
13207 /**
13208  * Returns index within the collection of the passed key.
13209  * @param {String} key The key to find the index of.
13210  * @return {Number} index of the key.
13211  */
13212     indexOfKey : function(key){
13213         if(!this.keys.indexOf){
13214             for(var i = 0, len = this.keys.length; i < len; i++){
13215                 if(this.keys[i] == key) {
13216                     return i;
13217                 }
13218             }
13219             return -1;
13220         }else{
13221             return this.keys.indexOf(key);
13222         }
13223     },
13224    
13225 /**
13226  * Returns the item associated with the passed key OR index. Key has priority over index.
13227  * @param {String/Number} key The key or index of the item.
13228  * @return {Object} The item associated with the passed key.
13229  */
13230     item : function(key){
13231         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13232         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13233     },
13234     
13235 /**
13236  * Returns the item at the specified index.
13237  * @param {Number} index The index of the item.
13238  * @return {Object}
13239  */
13240     itemAt : function(index){
13241         return this.items[index];
13242     },
13243     
13244 /**
13245  * Returns the item associated with the passed key.
13246  * @param {String/Number} key The key of the item.
13247  * @return {Object} The item associated with the passed key.
13248  */
13249     key : function(key){
13250         return this.map[key];
13251     },
13252    
13253 /**
13254  * Returns true if the collection contains the passed Object as an item.
13255  * @param {Object} o  The Object to look for in the collection.
13256  * @return {Boolean} True if the collection contains the Object as an item.
13257  */
13258     contains : function(o){
13259         return this.indexOf(o) != -1;
13260     },
13261    
13262 /**
13263  * Returns true if the collection contains the passed Object as a key.
13264  * @param {String} key The key to look for in the collection.
13265  * @return {Boolean} True if the collection contains the Object as a key.
13266  */
13267     containsKey : function(key){
13268         return typeof this.map[key] != "undefined";
13269     },
13270    
13271 /**
13272  * Removes all items from the collection.
13273  */
13274     clear : function(){
13275         this.length = 0;
13276         this.items = [];
13277         this.keys = [];
13278         this.map = {};
13279         this.fireEvent("clear");
13280     },
13281    
13282 /**
13283  * Returns the first item in the collection.
13284  * @return {Object} the first item in the collection..
13285  */
13286     first : function(){
13287         return this.items[0]; 
13288     },
13289    
13290 /**
13291  * Returns the last item in the collection.
13292  * @return {Object} the last item in the collection..
13293  */
13294     last : function(){
13295         return this.items[this.length-1];   
13296     },
13297     
13298     _sort : function(property, dir, fn){
13299         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13300         fn = fn || function(a, b){
13301             return a-b;
13302         };
13303         var c = [], k = this.keys, items = this.items;
13304         for(var i = 0, len = items.length; i < len; i++){
13305             c[c.length] = {key: k[i], value: items[i], index: i};
13306         }
13307         c.sort(function(a, b){
13308             var v = fn(a[property], b[property]) * dsc;
13309             if(v == 0){
13310                 v = (a.index < b.index ? -1 : 1);
13311             }
13312             return v;
13313         });
13314         for(var i = 0, len = c.length; i < len; i++){
13315             items[i] = c[i].value;
13316             k[i] = c[i].key;
13317         }
13318         this.fireEvent("sort", this);
13319     },
13320     
13321     /**
13322      * Sorts this collection with the passed comparison function
13323      * @param {String} direction (optional) "ASC" or "DESC"
13324      * @param {Function} fn (optional) comparison function
13325      */
13326     sort : function(dir, fn){
13327         this._sort("value", dir, fn);
13328     },
13329     
13330     /**
13331      * Sorts this collection by keys
13332      * @param {String} direction (optional) "ASC" or "DESC"
13333      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13334      */
13335     keySort : function(dir, fn){
13336         this._sort("key", dir, fn || function(a, b){
13337             return String(a).toUpperCase()-String(b).toUpperCase();
13338         });
13339     },
13340     
13341     /**
13342      * Returns a range of items in this collection
13343      * @param {Number} startIndex (optional) defaults to 0
13344      * @param {Number} endIndex (optional) default to the last item
13345      * @return {Array} An array of items
13346      */
13347     getRange : function(start, end){
13348         var items = this.items;
13349         if(items.length < 1){
13350             return [];
13351         }
13352         start = start || 0;
13353         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13354         var r = [];
13355         if(start <= end){
13356             for(var i = start; i <= end; i++) {
13357                     r[r.length] = items[i];
13358             }
13359         }else{
13360             for(var i = start; i >= end; i--) {
13361                     r[r.length] = items[i];
13362             }
13363         }
13364         return r;
13365     },
13366         
13367     /**
13368      * Filter the <i>objects</i> in this collection by a specific property. 
13369      * Returns a new collection that has been filtered.
13370      * @param {String} property A property on your objects
13371      * @param {String/RegExp} value Either string that the property values 
13372      * should start with or a RegExp to test against the property
13373      * @return {MixedCollection} The new filtered collection
13374      */
13375     filter : function(property, value){
13376         if(!value.exec){ // not a regex
13377             value = String(value);
13378             if(value.length == 0){
13379                 return this.clone();
13380             }
13381             value = new RegExp("^" + Roo.escapeRe(value), "i");
13382         }
13383         return this.filterBy(function(o){
13384             return o && value.test(o[property]);
13385         });
13386         },
13387     
13388     /**
13389      * Filter by a function. * Returns a new collection that has been filtered.
13390      * The passed function will be called with each 
13391      * object in the collection. If the function returns true, the value is included 
13392      * otherwise it is filtered.
13393      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13394      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13395      * @return {MixedCollection} The new filtered collection
13396      */
13397     filterBy : function(fn, scope){
13398         var r = new Roo.util.MixedCollection();
13399         r.getKey = this.getKey;
13400         var k = this.keys, it = this.items;
13401         for(var i = 0, len = it.length; i < len; i++){
13402             if(fn.call(scope||this, it[i], k[i])){
13403                                 r.add(k[i], it[i]);
13404                         }
13405         }
13406         return r;
13407     },
13408     
13409     /**
13410      * Creates a duplicate of this collection
13411      * @return {MixedCollection}
13412      */
13413     clone : function(){
13414         var r = new Roo.util.MixedCollection();
13415         var k = this.keys, it = this.items;
13416         for(var i = 0, len = it.length; i < len; i++){
13417             r.add(k[i], it[i]);
13418         }
13419         r.getKey = this.getKey;
13420         return r;
13421     }
13422 });
13423 /**
13424  * Returns the item associated with the passed key or index.
13425  * @method
13426  * @param {String/Number} key The key or index of the item.
13427  * @return {Object} The item associated with the passed key.
13428  */
13429 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13430  * Based on:
13431  * Ext JS Library 1.1.1
13432  * Copyright(c) 2006-2007, Ext JS, LLC.
13433  *
13434  * Originally Released Under LGPL - original licence link has changed is not relivant.
13435  *
13436  * Fork - LGPL
13437  * <script type="text/javascript">
13438  */
13439 /**
13440  * @class Roo.util.JSON
13441  * Modified version of Douglas Crockford"s json.js that doesn"t
13442  * mess with the Object prototype 
13443  * http://www.json.org/js.html
13444  * @singleton
13445  */
13446 Roo.util.JSON = new (function(){
13447     var useHasOwn = {}.hasOwnProperty ? true : false;
13448     
13449     // crashes Safari in some instances
13450     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13451     
13452     var pad = function(n) {
13453         return n < 10 ? "0" + n : n;
13454     };
13455     
13456     var m = {
13457         "\b": '\\b',
13458         "\t": '\\t',
13459         "\n": '\\n',
13460         "\f": '\\f',
13461         "\r": '\\r',
13462         '"' : '\\"',
13463         "\\": '\\\\'
13464     };
13465
13466     var encodeString = function(s){
13467         if (/["\\\x00-\x1f]/.test(s)) {
13468             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13469                 var c = m[b];
13470                 if(c){
13471                     return c;
13472                 }
13473                 c = b.charCodeAt();
13474                 return "\\u00" +
13475                     Math.floor(c / 16).toString(16) +
13476                     (c % 16).toString(16);
13477             }) + '"';
13478         }
13479         return '"' + s + '"';
13480     };
13481     
13482     var encodeArray = function(o){
13483         var a = ["["], b, i, l = o.length, v;
13484             for (i = 0; i < l; i += 1) {
13485                 v = o[i];
13486                 switch (typeof v) {
13487                     case "undefined":
13488                     case "function":
13489                     case "unknown":
13490                         break;
13491                     default:
13492                         if (b) {
13493                             a.push(',');
13494                         }
13495                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13496                         b = true;
13497                 }
13498             }
13499             a.push("]");
13500             return a.join("");
13501     };
13502     
13503     var encodeDate = function(o){
13504         return '"' + o.getFullYear() + "-" +
13505                 pad(o.getMonth() + 1) + "-" +
13506                 pad(o.getDate()) + "T" +
13507                 pad(o.getHours()) + ":" +
13508                 pad(o.getMinutes()) + ":" +
13509                 pad(o.getSeconds()) + '"';
13510     };
13511     
13512     /**
13513      * Encodes an Object, Array or other value
13514      * @param {Mixed} o The variable to encode
13515      * @return {String} The JSON string
13516      */
13517     this.encode = function(o)
13518     {
13519         // should this be extended to fully wrap stringify..
13520         
13521         if(typeof o == "undefined" || o === null){
13522             return "null";
13523         }else if(o instanceof Array){
13524             return encodeArray(o);
13525         }else if(o instanceof Date){
13526             return encodeDate(o);
13527         }else if(typeof o == "string"){
13528             return encodeString(o);
13529         }else if(typeof o == "number"){
13530             return isFinite(o) ? String(o) : "null";
13531         }else if(typeof o == "boolean"){
13532             return String(o);
13533         }else {
13534             var a = ["{"], b, i, v;
13535             for (i in o) {
13536                 if(!useHasOwn || o.hasOwnProperty(i)) {
13537                     v = o[i];
13538                     switch (typeof v) {
13539                     case "undefined":
13540                     case "function":
13541                     case "unknown":
13542                         break;
13543                     default:
13544                         if(b){
13545                             a.push(',');
13546                         }
13547                         a.push(this.encode(i), ":",
13548                                 v === null ? "null" : this.encode(v));
13549                         b = true;
13550                     }
13551                 }
13552             }
13553             a.push("}");
13554             return a.join("");
13555         }
13556     };
13557     
13558     /**
13559      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13560      * @param {String} json The JSON string
13561      * @return {Object} The resulting object
13562      */
13563     this.decode = function(json){
13564         
13565         return  /** eval:var:json */ eval("(" + json + ')');
13566     };
13567 })();
13568 /** 
13569  * Shorthand for {@link Roo.util.JSON#encode}
13570  * @member Roo encode 
13571  * @method */
13572 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13573 /** 
13574  * Shorthand for {@link Roo.util.JSON#decode}
13575  * @member Roo decode 
13576  * @method */
13577 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13578 /*
13579  * Based on:
13580  * Ext JS Library 1.1.1
13581  * Copyright(c) 2006-2007, Ext JS, LLC.
13582  *
13583  * Originally Released Under LGPL - original licence link has changed is not relivant.
13584  *
13585  * Fork - LGPL
13586  * <script type="text/javascript">
13587  */
13588  
13589 /**
13590  * @class Roo.util.Format
13591  * Reusable data formatting functions
13592  * @singleton
13593  */
13594 Roo.util.Format = function(){
13595     var trimRe = /^\s+|\s+$/g;
13596     return {
13597         /**
13598          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13599          * @param {String} value The string to truncate
13600          * @param {Number} length The maximum length to allow before truncating
13601          * @return {String} The converted text
13602          */
13603         ellipsis : function(value, len){
13604             if(value && value.length > len){
13605                 return value.substr(0, len-3)+"...";
13606             }
13607             return value;
13608         },
13609
13610         /**
13611          * Checks a reference and converts it to empty string if it is undefined
13612          * @param {Mixed} value Reference to check
13613          * @return {Mixed} Empty string if converted, otherwise the original value
13614          */
13615         undef : function(value){
13616             return typeof value != "undefined" ? value : "";
13617         },
13618
13619         /**
13620          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13621          * @param {String} value The string to encode
13622          * @return {String} The encoded text
13623          */
13624         htmlEncode : function(value){
13625             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13626         },
13627
13628         /**
13629          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13630          * @param {String} value The string to decode
13631          * @return {String} The decoded text
13632          */
13633         htmlDecode : function(value){
13634             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13635         },
13636
13637         /**
13638          * Trims any whitespace from either side of a string
13639          * @param {String} value The text to trim
13640          * @return {String} The trimmed text
13641          */
13642         trim : function(value){
13643             return String(value).replace(trimRe, "");
13644         },
13645
13646         /**
13647          * Returns a substring from within an original string
13648          * @param {String} value The original text
13649          * @param {Number} start The start index of the substring
13650          * @param {Number} length The length of the substring
13651          * @return {String} The substring
13652          */
13653         substr : function(value, start, length){
13654             return String(value).substr(start, length);
13655         },
13656
13657         /**
13658          * Converts a string to all lower case letters
13659          * @param {String} value The text to convert
13660          * @return {String} The converted text
13661          */
13662         lowercase : function(value){
13663             return String(value).toLowerCase();
13664         },
13665
13666         /**
13667          * Converts a string to all upper case letters
13668          * @param {String} value The text to convert
13669          * @return {String} The converted text
13670          */
13671         uppercase : function(value){
13672             return String(value).toUpperCase();
13673         },
13674
13675         /**
13676          * Converts the first character only of a string to upper case
13677          * @param {String} value The text to convert
13678          * @return {String} The converted text
13679          */
13680         capitalize : function(value){
13681             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13682         },
13683
13684         // private
13685         call : function(value, fn){
13686             if(arguments.length > 2){
13687                 var args = Array.prototype.slice.call(arguments, 2);
13688                 args.unshift(value);
13689                  
13690                 return /** eval:var:value */  eval(fn).apply(window, args);
13691             }else{
13692                 /** eval:var:value */
13693                 return /** eval:var:value */ eval(fn).call(window, value);
13694             }
13695         },
13696
13697        
13698         /**
13699          * safer version of Math.toFixed..??/
13700          * @param {Number/String} value The numeric value to format
13701          * @param {Number/String} value Decimal places 
13702          * @return {String} The formatted currency string
13703          */
13704         toFixed : function(v, n)
13705         {
13706             // why not use to fixed - precision is buggered???
13707             if (!n) {
13708                 return Math.round(v-0);
13709             }
13710             var fact = Math.pow(10,n+1);
13711             v = (Math.round((v-0)*fact))/fact;
13712             var z = (''+fact).substring(2);
13713             if (v == Math.floor(v)) {
13714                 return Math.floor(v) + '.' + z;
13715             }
13716             
13717             // now just padd decimals..
13718             var ps = String(v).split('.');
13719             var fd = (ps[1] + z);
13720             var r = fd.substring(0,n); 
13721             var rm = fd.substring(n); 
13722             if (rm < 5) {
13723                 return ps[0] + '.' + r;
13724             }
13725             r*=1; // turn it into a number;
13726             r++;
13727             if (String(r).length != n) {
13728                 ps[0]*=1;
13729                 ps[0]++;
13730                 r = String(r).substring(1); // chop the end off.
13731             }
13732             
13733             return ps[0] + '.' + r;
13734              
13735         },
13736         
13737         /**
13738          * Format a number as US currency
13739          * @param {Number/String} value The numeric value to format
13740          * @return {String} The formatted currency string
13741          */
13742         usMoney : function(v){
13743             return '$' + Roo.util.Format.number(v);
13744         },
13745         
13746         /**
13747          * Format a number
13748          * eventually this should probably emulate php's number_format
13749          * @param {Number/String} value The numeric value to format
13750          * @param {Number} decimals number of decimal places
13751          * @return {String} The formatted currency string
13752          */
13753         number : function(v,decimals)
13754         {
13755             // multiply and round.
13756             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13757             var mul = Math.pow(10, decimals);
13758             var zero = String(mul).substring(1);
13759             v = (Math.round((v-0)*mul))/mul;
13760             
13761             // if it's '0' number.. then
13762             
13763             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13764             v = String(v);
13765             var ps = v.split('.');
13766             var whole = ps[0];
13767             
13768             
13769             var r = /(\d+)(\d{3})/;
13770             // add comma's
13771             while (r.test(whole)) {
13772                 whole = whole.replace(r, '$1' + ',' + '$2');
13773             }
13774             
13775             
13776             var sub = ps[1] ?
13777                     // has decimals..
13778                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13779                     // does not have decimals
13780                     (decimals ? ('.' + zero) : '');
13781             
13782             
13783             return whole + sub ;
13784         },
13785         
13786         /**
13787          * Parse a value into a formatted date using the specified format pattern.
13788          * @param {Mixed} value The value to format
13789          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13790          * @return {String} The formatted date string
13791          */
13792         date : function(v, format){
13793             if(!v){
13794                 return "";
13795             }
13796             if(!(v instanceof Date)){
13797                 v = new Date(Date.parse(v));
13798             }
13799             return v.dateFormat(format || Roo.util.Format.defaults.date);
13800         },
13801
13802         /**
13803          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13804          * @param {String} format Any valid date format string
13805          * @return {Function} The date formatting function
13806          */
13807         dateRenderer : function(format){
13808             return function(v){
13809                 return Roo.util.Format.date(v, format);  
13810             };
13811         },
13812
13813         // private
13814         stripTagsRE : /<\/?[^>]+>/gi,
13815         
13816         /**
13817          * Strips all HTML tags
13818          * @param {Mixed} value The text from which to strip tags
13819          * @return {String} The stripped text
13820          */
13821         stripTags : function(v){
13822             return !v ? v : String(v).replace(this.stripTagsRE, "");
13823         }
13824     };
13825 }();
13826 Roo.util.Format.defaults = {
13827     date : 'd/M/Y'
13828 };/*
13829  * Based on:
13830  * Ext JS Library 1.1.1
13831  * Copyright(c) 2006-2007, Ext JS, LLC.
13832  *
13833  * Originally Released Under LGPL - original licence link has changed is not relivant.
13834  *
13835  * Fork - LGPL
13836  * <script type="text/javascript">
13837  */
13838
13839
13840  
13841
13842 /**
13843  * @class Roo.MasterTemplate
13844  * @extends Roo.Template
13845  * Provides a template that can have child templates. The syntax is:
13846 <pre><code>
13847 var t = new Roo.MasterTemplate(
13848         '&lt;select name="{name}"&gt;',
13849                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13850         '&lt;/select&gt;'
13851 );
13852 t.add('options', {value: 'foo', text: 'bar'});
13853 // or you can add multiple child elements in one shot
13854 t.addAll('options', [
13855     {value: 'foo', text: 'bar'},
13856     {value: 'foo2', text: 'bar2'},
13857     {value: 'foo3', text: 'bar3'}
13858 ]);
13859 // then append, applying the master template values
13860 t.append('my-form', {name: 'my-select'});
13861 </code></pre>
13862 * A name attribute for the child template is not required if you have only one child
13863 * template or you want to refer to them by index.
13864  */
13865 Roo.MasterTemplate = function(){
13866     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13867     this.originalHtml = this.html;
13868     var st = {};
13869     var m, re = this.subTemplateRe;
13870     re.lastIndex = 0;
13871     var subIndex = 0;
13872     while(m = re.exec(this.html)){
13873         var name = m[1], content = m[2];
13874         st[subIndex] = {
13875             name: name,
13876             index: subIndex,
13877             buffer: [],
13878             tpl : new Roo.Template(content)
13879         };
13880         if(name){
13881             st[name] = st[subIndex];
13882         }
13883         st[subIndex].tpl.compile();
13884         st[subIndex].tpl.call = this.call.createDelegate(this);
13885         subIndex++;
13886     }
13887     this.subCount = subIndex;
13888     this.subs = st;
13889 };
13890 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13891     /**
13892     * The regular expression used to match sub templates
13893     * @type RegExp
13894     * @property
13895     */
13896     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13897
13898     /**
13899      * Applies the passed values to a child template.
13900      * @param {String/Number} name (optional) The name or index of the child template
13901      * @param {Array/Object} values The values to be applied to the template
13902      * @return {MasterTemplate} this
13903      */
13904      add : function(name, values){
13905         if(arguments.length == 1){
13906             values = arguments[0];
13907             name = 0;
13908         }
13909         var s = this.subs[name];
13910         s.buffer[s.buffer.length] = s.tpl.apply(values);
13911         return this;
13912     },
13913
13914     /**
13915      * Applies all the passed values to a child template.
13916      * @param {String/Number} name (optional) The name or index of the child template
13917      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13918      * @param {Boolean} reset (optional) True to reset the template first
13919      * @return {MasterTemplate} this
13920      */
13921     fill : function(name, values, reset){
13922         var a = arguments;
13923         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13924             values = a[0];
13925             name = 0;
13926             reset = a[1];
13927         }
13928         if(reset){
13929             this.reset();
13930         }
13931         for(var i = 0, len = values.length; i < len; i++){
13932             this.add(name, values[i]);
13933         }
13934         return this;
13935     },
13936
13937     /**
13938      * Resets the template for reuse
13939      * @return {MasterTemplate} this
13940      */
13941      reset : function(){
13942         var s = this.subs;
13943         for(var i = 0; i < this.subCount; i++){
13944             s[i].buffer = [];
13945         }
13946         return this;
13947     },
13948
13949     applyTemplate : function(values){
13950         var s = this.subs;
13951         var replaceIndex = -1;
13952         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13953             return s[++replaceIndex].buffer.join("");
13954         });
13955         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13956     },
13957
13958     apply : function(){
13959         return this.applyTemplate.apply(this, arguments);
13960     },
13961
13962     compile : function(){return this;}
13963 });
13964
13965 /**
13966  * Alias for fill().
13967  * @method
13968  */
13969 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13970  /**
13971  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13972  * var tpl = Roo.MasterTemplate.from('element-id');
13973  * @param {String/HTMLElement} el
13974  * @param {Object} config
13975  * @static
13976  */
13977 Roo.MasterTemplate.from = function(el, config){
13978     el = Roo.getDom(el);
13979     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13980 };/*
13981  * Based on:
13982  * Ext JS Library 1.1.1
13983  * Copyright(c) 2006-2007, Ext JS, LLC.
13984  *
13985  * Originally Released Under LGPL - original licence link has changed is not relivant.
13986  *
13987  * Fork - LGPL
13988  * <script type="text/javascript">
13989  */
13990
13991  
13992 /**
13993  * @class Roo.util.CSS
13994  * Utility class for manipulating CSS rules
13995  * @singleton
13996  */
13997 Roo.util.CSS = function(){
13998         var rules = null;
13999         var doc = document;
14000
14001     var camelRe = /(-[a-z])/gi;
14002     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14003
14004    return {
14005    /**
14006     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14007     * tag and appended to the HEAD of the document.
14008     * @param {String|Object} cssText The text containing the css rules
14009     * @param {String} id An id to add to the stylesheet for later removal
14010     * @return {StyleSheet}
14011     */
14012     createStyleSheet : function(cssText, id){
14013         var ss;
14014         var head = doc.getElementsByTagName("head")[0];
14015         var nrules = doc.createElement("style");
14016         nrules.setAttribute("type", "text/css");
14017         if(id){
14018             nrules.setAttribute("id", id);
14019         }
14020         if (typeof(cssText) != 'string') {
14021             // support object maps..
14022             // not sure if this a good idea.. 
14023             // perhaps it should be merged with the general css handling
14024             // and handle js style props.
14025             var cssTextNew = [];
14026             for(var n in cssText) {
14027                 var citems = [];
14028                 for(var k in cssText[n]) {
14029                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14030                 }
14031                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14032                 
14033             }
14034             cssText = cssTextNew.join("\n");
14035             
14036         }
14037        
14038        
14039        if(Roo.isIE){
14040            head.appendChild(nrules);
14041            ss = nrules.styleSheet;
14042            ss.cssText = cssText;
14043        }else{
14044            try{
14045                 nrules.appendChild(doc.createTextNode(cssText));
14046            }catch(e){
14047                nrules.cssText = cssText; 
14048            }
14049            head.appendChild(nrules);
14050            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14051        }
14052        this.cacheStyleSheet(ss);
14053        return ss;
14054    },
14055
14056    /**
14057     * Removes a style or link tag by id
14058     * @param {String} id The id of the tag
14059     */
14060    removeStyleSheet : function(id){
14061        var existing = doc.getElementById(id);
14062        if(existing){
14063            existing.parentNode.removeChild(existing);
14064        }
14065    },
14066
14067    /**
14068     * Dynamically swaps an existing stylesheet reference for a new one
14069     * @param {String} id The id of an existing link tag to remove
14070     * @param {String} url The href of the new stylesheet to include
14071     */
14072    swapStyleSheet : function(id, url){
14073        this.removeStyleSheet(id);
14074        var ss = doc.createElement("link");
14075        ss.setAttribute("rel", "stylesheet");
14076        ss.setAttribute("type", "text/css");
14077        ss.setAttribute("id", id);
14078        ss.setAttribute("href", url);
14079        doc.getElementsByTagName("head")[0].appendChild(ss);
14080    },
14081    
14082    /**
14083     * Refresh the rule cache if you have dynamically added stylesheets
14084     * @return {Object} An object (hash) of rules indexed by selector
14085     */
14086    refreshCache : function(){
14087        return this.getRules(true);
14088    },
14089
14090    // private
14091    cacheStyleSheet : function(stylesheet){
14092        if(!rules){
14093            rules = {};
14094        }
14095        try{// try catch for cross domain access issue
14096            var ssRules = stylesheet.cssRules || stylesheet.rules;
14097            for(var j = ssRules.length-1; j >= 0; --j){
14098                rules[ssRules[j].selectorText] = ssRules[j];
14099            }
14100        }catch(e){}
14101    },
14102    
14103    /**
14104     * Gets all css rules for the document
14105     * @param {Boolean} refreshCache true to refresh the internal cache
14106     * @return {Object} An object (hash) of rules indexed by selector
14107     */
14108    getRules : function(refreshCache){
14109                 if(rules == null || refreshCache){
14110                         rules = {};
14111                         var ds = doc.styleSheets;
14112                         for(var i =0, len = ds.length; i < len; i++){
14113                             try{
14114                         this.cacheStyleSheet(ds[i]);
14115                     }catch(e){} 
14116                 }
14117                 }
14118                 return rules;
14119         },
14120         
14121         /**
14122     * Gets an an individual CSS rule by selector(s)
14123     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14124     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14125     * @return {CSSRule} The CSS rule or null if one is not found
14126     */
14127    getRule : function(selector, refreshCache){
14128                 var rs = this.getRules(refreshCache);
14129                 if(!(selector instanceof Array)){
14130                     return rs[selector];
14131                 }
14132                 for(var i = 0; i < selector.length; i++){
14133                         if(rs[selector[i]]){
14134                                 return rs[selector[i]];
14135                         }
14136                 }
14137                 return null;
14138         },
14139         
14140         
14141         /**
14142     * Updates a rule property
14143     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14144     * @param {String} property The css property
14145     * @param {String} value The new value for the property
14146     * @return {Boolean} true If a rule was found and updated
14147     */
14148    updateRule : function(selector, property, value){
14149                 if(!(selector instanceof Array)){
14150                         var rule = this.getRule(selector);
14151                         if(rule){
14152                                 rule.style[property.replace(camelRe, camelFn)] = value;
14153                                 return true;
14154                         }
14155                 }else{
14156                         for(var i = 0; i < selector.length; i++){
14157                                 if(this.updateRule(selector[i], property, value)){
14158                                         return true;
14159                                 }
14160                         }
14161                 }
14162                 return false;
14163         }
14164    };   
14165 }();/*
14166  * Based on:
14167  * Ext JS Library 1.1.1
14168  * Copyright(c) 2006-2007, Ext JS, LLC.
14169  *
14170  * Originally Released Under LGPL - original licence link has changed is not relivant.
14171  *
14172  * Fork - LGPL
14173  * <script type="text/javascript">
14174  */
14175
14176  
14177
14178 /**
14179  * @class Roo.util.ClickRepeater
14180  * @extends Roo.util.Observable
14181  * 
14182  * A wrapper class which can be applied to any element. Fires a "click" event while the
14183  * mouse is pressed. The interval between firings may be specified in the config but
14184  * defaults to 10 milliseconds.
14185  * 
14186  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14187  * 
14188  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14189  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14190  * Similar to an autorepeat key delay.
14191  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14192  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14193  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14194  *           "interval" and "delay" are ignored. "immediate" is honored.
14195  * @cfg {Boolean} preventDefault True to prevent the default click event
14196  * @cfg {Boolean} stopDefault True to stop the default click event
14197  * 
14198  * @history
14199  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14200  *     2007-02-02 jvs Renamed to ClickRepeater
14201  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14202  *
14203  *  @constructor
14204  * @param {String/HTMLElement/Element} el The element to listen on
14205  * @param {Object} config
14206  **/
14207 Roo.util.ClickRepeater = function(el, config)
14208 {
14209     this.el = Roo.get(el);
14210     this.el.unselectable();
14211
14212     Roo.apply(this, config);
14213
14214     this.addEvents({
14215     /**
14216      * @event mousedown
14217      * Fires when the mouse button is depressed.
14218      * @param {Roo.util.ClickRepeater} this
14219      */
14220         "mousedown" : true,
14221     /**
14222      * @event click
14223      * Fires on a specified interval during the time the element is pressed.
14224      * @param {Roo.util.ClickRepeater} this
14225      */
14226         "click" : true,
14227     /**
14228      * @event mouseup
14229      * Fires when the mouse key is released.
14230      * @param {Roo.util.ClickRepeater} this
14231      */
14232         "mouseup" : true
14233     });
14234
14235     this.el.on("mousedown", this.handleMouseDown, this);
14236     if(this.preventDefault || this.stopDefault){
14237         this.el.on("click", function(e){
14238             if(this.preventDefault){
14239                 e.preventDefault();
14240             }
14241             if(this.stopDefault){
14242                 e.stopEvent();
14243             }
14244         }, this);
14245     }
14246
14247     // allow inline handler
14248     if(this.handler){
14249         this.on("click", this.handler,  this.scope || this);
14250     }
14251
14252     Roo.util.ClickRepeater.superclass.constructor.call(this);
14253 };
14254
14255 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14256     interval : 20,
14257     delay: 250,
14258     preventDefault : true,
14259     stopDefault : false,
14260     timer : 0,
14261
14262     // private
14263     handleMouseDown : function(){
14264         clearTimeout(this.timer);
14265         this.el.blur();
14266         if(this.pressClass){
14267             this.el.addClass(this.pressClass);
14268         }
14269         this.mousedownTime = new Date();
14270
14271         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14272         this.el.on("mouseout", this.handleMouseOut, this);
14273
14274         this.fireEvent("mousedown", this);
14275         this.fireEvent("click", this);
14276         
14277         this.timer = this.click.defer(this.delay || this.interval, this);
14278     },
14279
14280     // private
14281     click : function(){
14282         this.fireEvent("click", this);
14283         this.timer = this.click.defer(this.getInterval(), this);
14284     },
14285
14286     // private
14287     getInterval: function(){
14288         if(!this.accelerate){
14289             return this.interval;
14290         }
14291         var pressTime = this.mousedownTime.getElapsed();
14292         if(pressTime < 500){
14293             return 400;
14294         }else if(pressTime < 1700){
14295             return 320;
14296         }else if(pressTime < 2600){
14297             return 250;
14298         }else if(pressTime < 3500){
14299             return 180;
14300         }else if(pressTime < 4400){
14301             return 140;
14302         }else if(pressTime < 5300){
14303             return 80;
14304         }else if(pressTime < 6200){
14305             return 50;
14306         }else{
14307             return 10;
14308         }
14309     },
14310
14311     // private
14312     handleMouseOut : function(){
14313         clearTimeout(this.timer);
14314         if(this.pressClass){
14315             this.el.removeClass(this.pressClass);
14316         }
14317         this.el.on("mouseover", this.handleMouseReturn, this);
14318     },
14319
14320     // private
14321     handleMouseReturn : function(){
14322         this.el.un("mouseover", this.handleMouseReturn);
14323         if(this.pressClass){
14324             this.el.addClass(this.pressClass);
14325         }
14326         this.click();
14327     },
14328
14329     // private
14330     handleMouseUp : function(){
14331         clearTimeout(this.timer);
14332         this.el.un("mouseover", this.handleMouseReturn);
14333         this.el.un("mouseout", this.handleMouseOut);
14334         Roo.get(document).un("mouseup", this.handleMouseUp);
14335         this.el.removeClass(this.pressClass);
14336         this.fireEvent("mouseup", this);
14337     }
14338 });/*
14339  * Based on:
14340  * Ext JS Library 1.1.1
14341  * Copyright(c) 2006-2007, Ext JS, LLC.
14342  *
14343  * Originally Released Under LGPL - original licence link has changed is not relivant.
14344  *
14345  * Fork - LGPL
14346  * <script type="text/javascript">
14347  */
14348
14349  
14350 /**
14351  * @class Roo.KeyNav
14352  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14353  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14354  * way to implement custom navigation schemes for any UI component.</p>
14355  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14356  * pageUp, pageDown, del, home, end.  Usage:</p>
14357  <pre><code>
14358 var nav = new Roo.KeyNav("my-element", {
14359     "left" : function(e){
14360         this.moveLeft(e.ctrlKey);
14361     },
14362     "right" : function(e){
14363         this.moveRight(e.ctrlKey);
14364     },
14365     "enter" : function(e){
14366         this.save();
14367     },
14368     scope : this
14369 });
14370 </code></pre>
14371  * @constructor
14372  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14373  * @param {Object} config The config
14374  */
14375 Roo.KeyNav = function(el, config){
14376     this.el = Roo.get(el);
14377     Roo.apply(this, config);
14378     if(!this.disabled){
14379         this.disabled = true;
14380         this.enable();
14381     }
14382 };
14383
14384 Roo.KeyNav.prototype = {
14385     /**
14386      * @cfg {Boolean} disabled
14387      * True to disable this KeyNav instance (defaults to false)
14388      */
14389     disabled : false,
14390     /**
14391      * @cfg {String} defaultEventAction
14392      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14393      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14394      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14395      */
14396     defaultEventAction: "stopEvent",
14397     /**
14398      * @cfg {Boolean} forceKeyDown
14399      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14400      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14401      * handle keydown instead of keypress.
14402      */
14403     forceKeyDown : false,
14404
14405     // private
14406     prepareEvent : function(e){
14407         var k = e.getKey();
14408         var h = this.keyToHandler[k];
14409         //if(h && this[h]){
14410         //    e.stopPropagation();
14411         //}
14412         if(Roo.isSafari && h && k >= 37 && k <= 40){
14413             e.stopEvent();
14414         }
14415     },
14416
14417     // private
14418     relay : function(e){
14419         var k = e.getKey();
14420         var h = this.keyToHandler[k];
14421         if(h && this[h]){
14422             if(this.doRelay(e, this[h], h) !== true){
14423                 e[this.defaultEventAction]();
14424             }
14425         }
14426     },
14427
14428     // private
14429     doRelay : function(e, h, hname){
14430         return h.call(this.scope || this, e);
14431     },
14432
14433     // possible handlers
14434     enter : false,
14435     left : false,
14436     right : false,
14437     up : false,
14438     down : false,
14439     tab : false,
14440     esc : false,
14441     pageUp : false,
14442     pageDown : false,
14443     del : false,
14444     home : false,
14445     end : false,
14446
14447     // quick lookup hash
14448     keyToHandler : {
14449         37 : "left",
14450         39 : "right",
14451         38 : "up",
14452         40 : "down",
14453         33 : "pageUp",
14454         34 : "pageDown",
14455         46 : "del",
14456         36 : "home",
14457         35 : "end",
14458         13 : "enter",
14459         27 : "esc",
14460         9  : "tab"
14461     },
14462
14463         /**
14464          * Enable this KeyNav
14465          */
14466         enable: function(){
14467                 if(this.disabled){
14468             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14469             // the EventObject will normalize Safari automatically
14470             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14471                 this.el.on("keydown", this.relay,  this);
14472             }else{
14473                 this.el.on("keydown", this.prepareEvent,  this);
14474                 this.el.on("keypress", this.relay,  this);
14475             }
14476                     this.disabled = false;
14477                 }
14478         },
14479
14480         /**
14481          * Disable this KeyNav
14482          */
14483         disable: function(){
14484                 if(!this.disabled){
14485                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14486                 this.el.un("keydown", this.relay);
14487             }else{
14488                 this.el.un("keydown", this.prepareEvent);
14489                 this.el.un("keypress", this.relay);
14490             }
14491                     this.disabled = true;
14492                 }
14493         }
14494 };/*
14495  * Based on:
14496  * Ext JS Library 1.1.1
14497  * Copyright(c) 2006-2007, Ext JS, LLC.
14498  *
14499  * Originally Released Under LGPL - original licence link has changed is not relivant.
14500  *
14501  * Fork - LGPL
14502  * <script type="text/javascript">
14503  */
14504
14505  
14506 /**
14507  * @class Roo.KeyMap
14508  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14509  * The constructor accepts the same config object as defined by {@link #addBinding}.
14510  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14511  * combination it will call the function with this signature (if the match is a multi-key
14512  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14513  * A KeyMap can also handle a string representation of keys.<br />
14514  * Usage:
14515  <pre><code>
14516 // map one key by key code
14517 var map = new Roo.KeyMap("my-element", {
14518     key: 13, // or Roo.EventObject.ENTER
14519     fn: myHandler,
14520     scope: myObject
14521 });
14522
14523 // map multiple keys to one action by string
14524 var map = new Roo.KeyMap("my-element", {
14525     key: "a\r\n\t",
14526     fn: myHandler,
14527     scope: myObject
14528 });
14529
14530 // map multiple keys to multiple actions by strings and array of codes
14531 var map = new Roo.KeyMap("my-element", [
14532     {
14533         key: [10,13],
14534         fn: function(){ alert("Return was pressed"); }
14535     }, {
14536         key: "abc",
14537         fn: function(){ alert('a, b or c was pressed'); }
14538     }, {
14539         key: "\t",
14540         ctrl:true,
14541         shift:true,
14542         fn: function(){ alert('Control + shift + tab was pressed.'); }
14543     }
14544 ]);
14545 </code></pre>
14546  * <b>Note: A KeyMap starts enabled</b>
14547  * @constructor
14548  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14549  * @param {Object} config The config (see {@link #addBinding})
14550  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14551  */
14552 Roo.KeyMap = function(el, config, eventName){
14553     this.el  = Roo.get(el);
14554     this.eventName = eventName || "keydown";
14555     this.bindings = [];
14556     if(config){
14557         this.addBinding(config);
14558     }
14559     this.enable();
14560 };
14561
14562 Roo.KeyMap.prototype = {
14563     /**
14564      * True to stop the event from bubbling and prevent the default browser action if the
14565      * key was handled by the KeyMap (defaults to false)
14566      * @type Boolean
14567      */
14568     stopEvent : false,
14569
14570     /**
14571      * Add a new binding to this KeyMap. The following config object properties are supported:
14572      * <pre>
14573 Property    Type             Description
14574 ----------  ---------------  ----------------------------------------------------------------------
14575 key         String/Array     A single keycode or an array of keycodes to handle
14576 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14577 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14578 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14579 fn          Function         The function to call when KeyMap finds the expected key combination
14580 scope       Object           The scope of the callback function
14581 </pre>
14582      *
14583      * Usage:
14584      * <pre><code>
14585 // Create a KeyMap
14586 var map = new Roo.KeyMap(document, {
14587     key: Roo.EventObject.ENTER,
14588     fn: handleKey,
14589     scope: this
14590 });
14591
14592 //Add a new binding to the existing KeyMap later
14593 map.addBinding({
14594     key: 'abc',
14595     shift: true,
14596     fn: handleKey,
14597     scope: this
14598 });
14599 </code></pre>
14600      * @param {Object/Array} config A single KeyMap config or an array of configs
14601      */
14602         addBinding : function(config){
14603         if(config instanceof Array){
14604             for(var i = 0, len = config.length; i < len; i++){
14605                 this.addBinding(config[i]);
14606             }
14607             return;
14608         }
14609         var keyCode = config.key,
14610             shift = config.shift, 
14611             ctrl = config.ctrl, 
14612             alt = config.alt,
14613             fn = config.fn,
14614             scope = config.scope;
14615         if(typeof keyCode == "string"){
14616             var ks = [];
14617             var keyString = keyCode.toUpperCase();
14618             for(var j = 0, len = keyString.length; j < len; j++){
14619                 ks.push(keyString.charCodeAt(j));
14620             }
14621             keyCode = ks;
14622         }
14623         var keyArray = keyCode instanceof Array;
14624         var handler = function(e){
14625             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14626                 var k = e.getKey();
14627                 if(keyArray){
14628                     for(var i = 0, len = keyCode.length; i < len; i++){
14629                         if(keyCode[i] == k){
14630                           if(this.stopEvent){
14631                               e.stopEvent();
14632                           }
14633                           fn.call(scope || window, k, e);
14634                           return;
14635                         }
14636                     }
14637                 }else{
14638                     if(k == keyCode){
14639                         if(this.stopEvent){
14640                            e.stopEvent();
14641                         }
14642                         fn.call(scope || window, k, e);
14643                     }
14644                 }
14645             }
14646         };
14647         this.bindings.push(handler);  
14648         },
14649
14650     /**
14651      * Shorthand for adding a single key listener
14652      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14653      * following options:
14654      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14655      * @param {Function} fn The function to call
14656      * @param {Object} scope (optional) The scope of the function
14657      */
14658     on : function(key, fn, scope){
14659         var keyCode, shift, ctrl, alt;
14660         if(typeof key == "object" && !(key instanceof Array)){
14661             keyCode = key.key;
14662             shift = key.shift;
14663             ctrl = key.ctrl;
14664             alt = key.alt;
14665         }else{
14666             keyCode = key;
14667         }
14668         this.addBinding({
14669             key: keyCode,
14670             shift: shift,
14671             ctrl: ctrl,
14672             alt: alt,
14673             fn: fn,
14674             scope: scope
14675         })
14676     },
14677
14678     // private
14679     handleKeyDown : function(e){
14680             if(this.enabled){ //just in case
14681             var b = this.bindings;
14682             for(var i = 0, len = b.length; i < len; i++){
14683                 b[i].call(this, e);
14684             }
14685             }
14686         },
14687         
14688         /**
14689          * Returns true if this KeyMap is enabled
14690          * @return {Boolean} 
14691          */
14692         isEnabled : function(){
14693             return this.enabled;  
14694         },
14695         
14696         /**
14697          * Enables this KeyMap
14698          */
14699         enable: function(){
14700                 if(!this.enabled){
14701                     this.el.on(this.eventName, this.handleKeyDown, this);
14702                     this.enabled = true;
14703                 }
14704         },
14705
14706         /**
14707          * Disable this KeyMap
14708          */
14709         disable: function(){
14710                 if(this.enabled){
14711                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14712                     this.enabled = false;
14713                 }
14714         }
14715 };/*
14716  * Based on:
14717  * Ext JS Library 1.1.1
14718  * Copyright(c) 2006-2007, Ext JS, LLC.
14719  *
14720  * Originally Released Under LGPL - original licence link has changed is not relivant.
14721  *
14722  * Fork - LGPL
14723  * <script type="text/javascript">
14724  */
14725
14726  
14727 /**
14728  * @class Roo.util.TextMetrics
14729  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14730  * wide, in pixels, a given block of text will be.
14731  * @singleton
14732  */
14733 Roo.util.TextMetrics = function(){
14734     var shared;
14735     return {
14736         /**
14737          * Measures the size of the specified text
14738          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14739          * that can affect the size of the rendered text
14740          * @param {String} text The text to measure
14741          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14742          * in order to accurately measure the text height
14743          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14744          */
14745         measure : function(el, text, fixedWidth){
14746             if(!shared){
14747                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14748             }
14749             shared.bind(el);
14750             shared.setFixedWidth(fixedWidth || 'auto');
14751             return shared.getSize(text);
14752         },
14753
14754         /**
14755          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14756          * the overhead of multiple calls to initialize the style properties on each measurement.
14757          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14758          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14759          * in order to accurately measure the text height
14760          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14761          */
14762         createInstance : function(el, fixedWidth){
14763             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14764         }
14765     };
14766 }();
14767
14768  
14769
14770 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14771     var ml = new Roo.Element(document.createElement('div'));
14772     document.body.appendChild(ml.dom);
14773     ml.position('absolute');
14774     ml.setLeftTop(-1000, -1000);
14775     ml.hide();
14776
14777     if(fixedWidth){
14778         ml.setWidth(fixedWidth);
14779     }
14780      
14781     var instance = {
14782         /**
14783          * Returns the size of the specified text based on the internal element's style and width properties
14784          * @memberOf Roo.util.TextMetrics.Instance#
14785          * @param {String} text The text to measure
14786          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14787          */
14788         getSize : function(text){
14789             ml.update(text);
14790             var s = ml.getSize();
14791             ml.update('');
14792             return s;
14793         },
14794
14795         /**
14796          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14797          * that can affect the size of the rendered text
14798          * @memberOf Roo.util.TextMetrics.Instance#
14799          * @param {String/HTMLElement} el The element, dom node or id
14800          */
14801         bind : function(el){
14802             ml.setStyle(
14803                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14804             );
14805         },
14806
14807         /**
14808          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14809          * to set a fixed width in order to accurately measure the text height.
14810          * @memberOf Roo.util.TextMetrics.Instance#
14811          * @param {Number} width The width to set on the element
14812          */
14813         setFixedWidth : function(width){
14814             ml.setWidth(width);
14815         },
14816
14817         /**
14818          * Returns the measured width of the specified text
14819          * @memberOf Roo.util.TextMetrics.Instance#
14820          * @param {String} text The text to measure
14821          * @return {Number} width The width in pixels
14822          */
14823         getWidth : function(text){
14824             ml.dom.style.width = 'auto';
14825             return this.getSize(text).width;
14826         },
14827
14828         /**
14829          * Returns the measured height of the specified text.  For multiline text, be sure to call
14830          * {@link #setFixedWidth} if necessary.
14831          * @memberOf Roo.util.TextMetrics.Instance#
14832          * @param {String} text The text to measure
14833          * @return {Number} height The height in pixels
14834          */
14835         getHeight : function(text){
14836             return this.getSize(text).height;
14837         }
14838     };
14839
14840     instance.bind(bindTo);
14841
14842     return instance;
14843 };
14844
14845 // backwards compat
14846 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14847  * Based on:
14848  * Ext JS Library 1.1.1
14849  * Copyright(c) 2006-2007, Ext JS, LLC.
14850  *
14851  * Originally Released Under LGPL - original licence link has changed is not relivant.
14852  *
14853  * Fork - LGPL
14854  * <script type="text/javascript">
14855  */
14856
14857 /**
14858  * @class Roo.state.Provider
14859  * Abstract base class for state provider implementations. This class provides methods
14860  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14861  * Provider interface.
14862  */
14863 Roo.state.Provider = function(){
14864     /**
14865      * @event statechange
14866      * Fires when a state change occurs.
14867      * @param {Provider} this This state provider
14868      * @param {String} key The state key which was changed
14869      * @param {String} value The encoded value for the state
14870      */
14871     this.addEvents({
14872         "statechange": true
14873     });
14874     this.state = {};
14875     Roo.state.Provider.superclass.constructor.call(this);
14876 };
14877 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14878     /**
14879      * Returns the current value for a key
14880      * @param {String} name The key name
14881      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14882      * @return {Mixed} The state data
14883      */
14884     get : function(name, defaultValue){
14885         return typeof this.state[name] == "undefined" ?
14886             defaultValue : this.state[name];
14887     },
14888     
14889     /**
14890      * Clears a value from the state
14891      * @param {String} name The key name
14892      */
14893     clear : function(name){
14894         delete this.state[name];
14895         this.fireEvent("statechange", this, name, null);
14896     },
14897     
14898     /**
14899      * Sets the value for a key
14900      * @param {String} name The key name
14901      * @param {Mixed} value The value to set
14902      */
14903     set : function(name, value){
14904         this.state[name] = value;
14905         this.fireEvent("statechange", this, name, value);
14906     },
14907     
14908     /**
14909      * Decodes a string previously encoded with {@link #encodeValue}.
14910      * @param {String} value The value to decode
14911      * @return {Mixed} The decoded value
14912      */
14913     decodeValue : function(cookie){
14914         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14915         var matches = re.exec(unescape(cookie));
14916         if(!matches || !matches[1]) {
14917             return; // non state cookie
14918         }
14919         var type = matches[1];
14920         var v = matches[2];
14921         switch(type){
14922             case "n":
14923                 return parseFloat(v);
14924             case "d":
14925                 return new Date(Date.parse(v));
14926             case "b":
14927                 return (v == "1");
14928             case "a":
14929                 var all = [];
14930                 var values = v.split("^");
14931                 for(var i = 0, len = values.length; i < len; i++){
14932                     all.push(this.decodeValue(values[i]));
14933                 }
14934                 return all;
14935            case "o":
14936                 var all = {};
14937                 var values = v.split("^");
14938                 for(var i = 0, len = values.length; i < len; i++){
14939                     var kv = values[i].split("=");
14940                     all[kv[0]] = this.decodeValue(kv[1]);
14941                 }
14942                 return all;
14943            default:
14944                 return v;
14945         }
14946     },
14947     
14948     /**
14949      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14950      * @param {Mixed} value The value to encode
14951      * @return {String} The encoded value
14952      */
14953     encodeValue : function(v){
14954         var enc;
14955         if(typeof v == "number"){
14956             enc = "n:" + v;
14957         }else if(typeof v == "boolean"){
14958             enc = "b:" + (v ? "1" : "0");
14959         }else if(v instanceof Date){
14960             enc = "d:" + v.toGMTString();
14961         }else if(v instanceof Array){
14962             var flat = "";
14963             for(var i = 0, len = v.length; i < len; i++){
14964                 flat += this.encodeValue(v[i]);
14965                 if(i != len-1) {
14966                     flat += "^";
14967                 }
14968             }
14969             enc = "a:" + flat;
14970         }else if(typeof v == "object"){
14971             var flat = "";
14972             for(var key in v){
14973                 if(typeof v[key] != "function"){
14974                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14975                 }
14976             }
14977             enc = "o:" + flat.substring(0, flat.length-1);
14978         }else{
14979             enc = "s:" + v;
14980         }
14981         return escape(enc);        
14982     }
14983 });
14984
14985 /*
14986  * Based on:
14987  * Ext JS Library 1.1.1
14988  * Copyright(c) 2006-2007, Ext JS, LLC.
14989  *
14990  * Originally Released Under LGPL - original licence link has changed is not relivant.
14991  *
14992  * Fork - LGPL
14993  * <script type="text/javascript">
14994  */
14995 /**
14996  * @class Roo.state.Manager
14997  * This is the global state manager. By default all components that are "state aware" check this class
14998  * for state information if you don't pass them a custom state provider. In order for this class
14999  * to be useful, it must be initialized with a provider when your application initializes.
15000  <pre><code>
15001 // in your initialization function
15002 init : function(){
15003    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
15004    ...
15005    // supposed you have a {@link Roo.BorderLayout}
15006    var layout = new Roo.BorderLayout(...);
15007    layout.restoreState();
15008    // or a {Roo.BasicDialog}
15009    var dialog = new Roo.BasicDialog(...);
15010    dialog.restoreState();
15011  </code></pre>
15012  * @singleton
15013  */
15014 Roo.state.Manager = function(){
15015     var provider = new Roo.state.Provider();
15016     
15017     return {
15018         /**
15019          * Configures the default state provider for your application
15020          * @param {Provider} stateProvider The state provider to set
15021          */
15022         setProvider : function(stateProvider){
15023             provider = stateProvider;
15024         },
15025         
15026         /**
15027          * Returns the current value for a key
15028          * @param {String} name The key name
15029          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15030          * @return {Mixed} The state data
15031          */
15032         get : function(key, defaultValue){
15033             return provider.get(key, defaultValue);
15034         },
15035         
15036         /**
15037          * Sets the value for a key
15038          * @param {String} name The key name
15039          * @param {Mixed} value The state data
15040          */
15041          set : function(key, value){
15042             provider.set(key, value);
15043         },
15044         
15045         /**
15046          * Clears a value from the state
15047          * @param {String} name The key name
15048          */
15049         clear : function(key){
15050             provider.clear(key);
15051         },
15052         
15053         /**
15054          * Gets the currently configured state provider
15055          * @return {Provider} The state provider
15056          */
15057         getProvider : function(){
15058             return provider;
15059         }
15060     };
15061 }();
15062 /*
15063  * Based on:
15064  * Ext JS Library 1.1.1
15065  * Copyright(c) 2006-2007, Ext JS, LLC.
15066  *
15067  * Originally Released Under LGPL - original licence link has changed is not relivant.
15068  *
15069  * Fork - LGPL
15070  * <script type="text/javascript">
15071  */
15072 /**
15073  * @class Roo.state.CookieProvider
15074  * @extends Roo.state.Provider
15075  * The default Provider implementation which saves state via cookies.
15076  * <br />Usage:
15077  <pre><code>
15078    var cp = new Roo.state.CookieProvider({
15079        path: "/cgi-bin/",
15080        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15081        domain: "roojs.com"
15082    })
15083    Roo.state.Manager.setProvider(cp);
15084  </code></pre>
15085  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15086  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15087  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15088  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15089  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15090  * domain the page is running on including the 'www' like 'www.roojs.com')
15091  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15092  * @constructor
15093  * Create a new CookieProvider
15094  * @param {Object} config The configuration object
15095  */
15096 Roo.state.CookieProvider = function(config){
15097     Roo.state.CookieProvider.superclass.constructor.call(this);
15098     this.path = "/";
15099     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15100     this.domain = null;
15101     this.secure = false;
15102     Roo.apply(this, config);
15103     this.state = this.readCookies();
15104 };
15105
15106 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15107     // private
15108     set : function(name, value){
15109         if(typeof value == "undefined" || value === null){
15110             this.clear(name);
15111             return;
15112         }
15113         this.setCookie(name, value);
15114         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15115     },
15116
15117     // private
15118     clear : function(name){
15119         this.clearCookie(name);
15120         Roo.state.CookieProvider.superclass.clear.call(this, name);
15121     },
15122
15123     // private
15124     readCookies : function(){
15125         var cookies = {};
15126         var c = document.cookie + ";";
15127         var re = /\s?(.*?)=(.*?);/g;
15128         var matches;
15129         while((matches = re.exec(c)) != null){
15130             var name = matches[1];
15131             var value = matches[2];
15132             if(name && name.substring(0,3) == "ys-"){
15133                 cookies[name.substr(3)] = this.decodeValue(value);
15134             }
15135         }
15136         return cookies;
15137     },
15138
15139     // private
15140     setCookie : function(name, value){
15141         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15142            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15143            ((this.path == null) ? "" : ("; path=" + this.path)) +
15144            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15145            ((this.secure == true) ? "; secure" : "");
15146     },
15147
15148     // private
15149     clearCookie : function(name){
15150         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15151            ((this.path == null) ? "" : ("; path=" + this.path)) +
15152            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15153            ((this.secure == true) ? "; secure" : "");
15154     }
15155 });/*
15156  * Based on:
15157  * Ext JS Library 1.1.1
15158  * Copyright(c) 2006-2007, Ext JS, LLC.
15159  *
15160  * Originally Released Under LGPL - original licence link has changed is not relivant.
15161  *
15162  * Fork - LGPL
15163  * <script type="text/javascript">
15164  */
15165  
15166
15167 /**
15168  * @class Roo.ComponentMgr
15169  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15170  * @singleton
15171  */
15172 Roo.ComponentMgr = function(){
15173     var all = new Roo.util.MixedCollection();
15174
15175     return {
15176         /**
15177          * Registers a component.
15178          * @param {Roo.Component} c The component
15179          */
15180         register : function(c){
15181             all.add(c);
15182         },
15183
15184         /**
15185          * Unregisters a component.
15186          * @param {Roo.Component} c The component
15187          */
15188         unregister : function(c){
15189             all.remove(c);
15190         },
15191
15192         /**
15193          * Returns a component by id
15194          * @param {String} id The component id
15195          */
15196         get : function(id){
15197             return all.get(id);
15198         },
15199
15200         /**
15201          * Registers a function that will be called when a specified component is added to ComponentMgr
15202          * @param {String} id The component id
15203          * @param {Funtction} fn The callback function
15204          * @param {Object} scope The scope of the callback
15205          */
15206         onAvailable : function(id, fn, scope){
15207             all.on("add", function(index, o){
15208                 if(o.id == id){
15209                     fn.call(scope || o, o);
15210                     all.un("add", fn, scope);
15211                 }
15212             });
15213         }
15214     };
15215 }();/*
15216  * Based on:
15217  * Ext JS Library 1.1.1
15218  * Copyright(c) 2006-2007, Ext JS, LLC.
15219  *
15220  * Originally Released Under LGPL - original licence link has changed is not relivant.
15221  *
15222  * Fork - LGPL
15223  * <script type="text/javascript">
15224  */
15225  
15226 /**
15227  * @class Roo.Component
15228  * @extends Roo.util.Observable
15229  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15230  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15231  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15232  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15233  * All visual components (widgets) that require rendering into a layout should subclass Component.
15234  * @constructor
15235  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15236  * 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
15237  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15238  */
15239 Roo.Component = function(config){
15240     config = config || {};
15241     if(config.tagName || config.dom || typeof config == "string"){ // element object
15242         config = {el: config, id: config.id || config};
15243     }
15244     this.initialConfig = config;
15245
15246     Roo.apply(this, config);
15247     this.addEvents({
15248         /**
15249          * @event disable
15250          * Fires after the component is disabled.
15251              * @param {Roo.Component} this
15252              */
15253         disable : true,
15254         /**
15255          * @event enable
15256          * Fires after the component is enabled.
15257              * @param {Roo.Component} this
15258              */
15259         enable : true,
15260         /**
15261          * @event beforeshow
15262          * Fires before the component is shown.  Return false to stop the show.
15263              * @param {Roo.Component} this
15264              */
15265         beforeshow : true,
15266         /**
15267          * @event show
15268          * Fires after the component is shown.
15269              * @param {Roo.Component} this
15270              */
15271         show : true,
15272         /**
15273          * @event beforehide
15274          * Fires before the component is hidden. Return false to stop the hide.
15275              * @param {Roo.Component} this
15276              */
15277         beforehide : true,
15278         /**
15279          * @event hide
15280          * Fires after the component is hidden.
15281              * @param {Roo.Component} this
15282              */
15283         hide : true,
15284         /**
15285          * @event beforerender
15286          * Fires before the component is rendered. Return false to stop the render.
15287              * @param {Roo.Component} this
15288              */
15289         beforerender : true,
15290         /**
15291          * @event render
15292          * Fires after the component is rendered.
15293              * @param {Roo.Component} this
15294              */
15295         render : true,
15296         /**
15297          * @event beforedestroy
15298          * Fires before the component is destroyed. Return false to stop the destroy.
15299              * @param {Roo.Component} this
15300              */
15301         beforedestroy : true,
15302         /**
15303          * @event destroy
15304          * Fires after the component is destroyed.
15305              * @param {Roo.Component} this
15306              */
15307         destroy : true
15308     });
15309     if(!this.id){
15310         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15311     }
15312     Roo.ComponentMgr.register(this);
15313     Roo.Component.superclass.constructor.call(this);
15314     this.initComponent();
15315     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15316         this.render(this.renderTo);
15317         delete this.renderTo;
15318     }
15319 };
15320
15321 /** @private */
15322 Roo.Component.AUTO_ID = 1000;
15323
15324 Roo.extend(Roo.Component, Roo.util.Observable, {
15325     /**
15326      * @scope Roo.Component.prototype
15327      * @type {Boolean}
15328      * true if this component is hidden. Read-only.
15329      */
15330     hidden : false,
15331     /**
15332      * @type {Boolean}
15333      * true if this component is disabled. Read-only.
15334      */
15335     disabled : false,
15336     /**
15337      * @type {Boolean}
15338      * true if this component has been rendered. Read-only.
15339      */
15340     rendered : false,
15341     
15342     /** @cfg {String} disableClass
15343      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15344      */
15345     disabledClass : "x-item-disabled",
15346         /** @cfg {Boolean} allowDomMove
15347          * Whether the component can move the Dom node when rendering (defaults to true).
15348          */
15349     allowDomMove : true,
15350     /** @cfg {String} hideMode (display|visibility)
15351      * How this component should hidden. Supported values are
15352      * "visibility" (css visibility), "offsets" (negative offset position) and
15353      * "display" (css display) - defaults to "display".
15354      */
15355     hideMode: 'display',
15356
15357     /** @private */
15358     ctype : "Roo.Component",
15359
15360     /**
15361      * @cfg {String} actionMode 
15362      * which property holds the element that used for  hide() / show() / disable() / enable()
15363      * default is 'el' 
15364      */
15365     actionMode : "el",
15366
15367     /** @private */
15368     getActionEl : function(){
15369         return this[this.actionMode];
15370     },
15371
15372     initComponent : Roo.emptyFn,
15373     /**
15374      * If this is a lazy rendering component, render it to its container element.
15375      * @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.
15376      */
15377     render : function(container, position){
15378         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15379             if(!container && this.el){
15380                 this.el = Roo.get(this.el);
15381                 container = this.el.dom.parentNode;
15382                 this.allowDomMove = false;
15383             }
15384             this.container = Roo.get(container);
15385             this.rendered = true;
15386             if(position !== undefined){
15387                 if(typeof position == 'number'){
15388                     position = this.container.dom.childNodes[position];
15389                 }else{
15390                     position = Roo.getDom(position);
15391                 }
15392             }
15393             this.onRender(this.container, position || null);
15394             if(this.cls){
15395                 this.el.addClass(this.cls);
15396                 delete this.cls;
15397             }
15398             if(this.style){
15399                 this.el.applyStyles(this.style);
15400                 delete this.style;
15401             }
15402             this.fireEvent("render", this);
15403             this.afterRender(this.container);
15404             if(this.hidden){
15405                 this.hide();
15406             }
15407             if(this.disabled){
15408                 this.disable();
15409             }
15410         }
15411         return this;
15412     },
15413
15414     /** @private */
15415     // default function is not really useful
15416     onRender : function(ct, position){
15417         if(this.el){
15418             this.el = Roo.get(this.el);
15419             if(this.allowDomMove !== false){
15420                 ct.dom.insertBefore(this.el.dom, position);
15421             }
15422         }
15423     },
15424
15425     /** @private */
15426     getAutoCreate : function(){
15427         var cfg = typeof this.autoCreate == "object" ?
15428                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15429         if(this.id && !cfg.id){
15430             cfg.id = this.id;
15431         }
15432         return cfg;
15433     },
15434
15435     /** @private */
15436     afterRender : Roo.emptyFn,
15437
15438     /**
15439      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15440      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15441      */
15442     destroy : function(){
15443         if(this.fireEvent("beforedestroy", this) !== false){
15444             this.purgeListeners();
15445             this.beforeDestroy();
15446             if(this.rendered){
15447                 this.el.removeAllListeners();
15448                 this.el.remove();
15449                 if(this.actionMode == "container"){
15450                     this.container.remove();
15451                 }
15452             }
15453             this.onDestroy();
15454             Roo.ComponentMgr.unregister(this);
15455             this.fireEvent("destroy", this);
15456         }
15457     },
15458
15459         /** @private */
15460     beforeDestroy : function(){
15461
15462     },
15463
15464         /** @private */
15465         onDestroy : function(){
15466
15467     },
15468
15469     /**
15470      * Returns the underlying {@link Roo.Element}.
15471      * @return {Roo.Element} The element
15472      */
15473     getEl : function(){
15474         return this.el;
15475     },
15476
15477     /**
15478      * Returns the id of this component.
15479      * @return {String}
15480      */
15481     getId : function(){
15482         return this.id;
15483     },
15484
15485     /**
15486      * Try to focus this component.
15487      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15488      * @return {Roo.Component} this
15489      */
15490     focus : function(selectText){
15491         if(this.rendered){
15492             this.el.focus();
15493             if(selectText === true){
15494                 this.el.dom.select();
15495             }
15496         }
15497         return this;
15498     },
15499
15500     /** @private */
15501     blur : function(){
15502         if(this.rendered){
15503             this.el.blur();
15504         }
15505         return this;
15506     },
15507
15508     /**
15509      * Disable this component.
15510      * @return {Roo.Component} this
15511      */
15512     disable : function(){
15513         if(this.rendered){
15514             this.onDisable();
15515         }
15516         this.disabled = true;
15517         this.fireEvent("disable", this);
15518         return this;
15519     },
15520
15521         // private
15522     onDisable : function(){
15523         this.getActionEl().addClass(this.disabledClass);
15524         this.el.dom.disabled = true;
15525     },
15526
15527     /**
15528      * Enable this component.
15529      * @return {Roo.Component} this
15530      */
15531     enable : function(){
15532         if(this.rendered){
15533             this.onEnable();
15534         }
15535         this.disabled = false;
15536         this.fireEvent("enable", this);
15537         return this;
15538     },
15539
15540         // private
15541     onEnable : function(){
15542         this.getActionEl().removeClass(this.disabledClass);
15543         this.el.dom.disabled = false;
15544     },
15545
15546     /**
15547      * Convenience function for setting disabled/enabled by boolean.
15548      * @param {Boolean} disabled
15549      */
15550     setDisabled : function(disabled){
15551         this[disabled ? "disable" : "enable"]();
15552     },
15553
15554     /**
15555      * Show this component.
15556      * @return {Roo.Component} this
15557      */
15558     show: function(){
15559         if(this.fireEvent("beforeshow", this) !== false){
15560             this.hidden = false;
15561             if(this.rendered){
15562                 this.onShow();
15563             }
15564             this.fireEvent("show", this);
15565         }
15566         return this;
15567     },
15568
15569     // private
15570     onShow : function(){
15571         var ae = this.getActionEl();
15572         if(this.hideMode == 'visibility'){
15573             ae.dom.style.visibility = "visible";
15574         }else if(this.hideMode == 'offsets'){
15575             ae.removeClass('x-hidden');
15576         }else{
15577             ae.dom.style.display = "";
15578         }
15579     },
15580
15581     /**
15582      * Hide this component.
15583      * @return {Roo.Component} this
15584      */
15585     hide: function(){
15586         if(this.fireEvent("beforehide", this) !== false){
15587             this.hidden = true;
15588             if(this.rendered){
15589                 this.onHide();
15590             }
15591             this.fireEvent("hide", this);
15592         }
15593         return this;
15594     },
15595
15596     // private
15597     onHide : function(){
15598         var ae = this.getActionEl();
15599         if(this.hideMode == 'visibility'){
15600             ae.dom.style.visibility = "hidden";
15601         }else if(this.hideMode == 'offsets'){
15602             ae.addClass('x-hidden');
15603         }else{
15604             ae.dom.style.display = "none";
15605         }
15606     },
15607
15608     /**
15609      * Convenience function to hide or show this component by boolean.
15610      * @param {Boolean} visible True to show, false to hide
15611      * @return {Roo.Component} this
15612      */
15613     setVisible: function(visible){
15614         if(visible) {
15615             this.show();
15616         }else{
15617             this.hide();
15618         }
15619         return this;
15620     },
15621
15622     /**
15623      * Returns true if this component is visible.
15624      */
15625     isVisible : function(){
15626         return this.getActionEl().isVisible();
15627     },
15628
15629     cloneConfig : function(overrides){
15630         overrides = overrides || {};
15631         var id = overrides.id || Roo.id();
15632         var cfg = Roo.applyIf(overrides, this.initialConfig);
15633         cfg.id = id; // prevent dup id
15634         return new this.constructor(cfg);
15635     }
15636 });/*
15637  * Based on:
15638  * Ext JS Library 1.1.1
15639  * Copyright(c) 2006-2007, Ext JS, LLC.
15640  *
15641  * Originally Released Under LGPL - original licence link has changed is not relivant.
15642  *
15643  * Fork - LGPL
15644  * <script type="text/javascript">
15645  */
15646
15647 /**
15648  * @class Roo.BoxComponent
15649  * @extends Roo.Component
15650  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15651  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15652  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15653  * layout containers.
15654  * @constructor
15655  * @param {Roo.Element/String/Object} config The configuration options.
15656  */
15657 Roo.BoxComponent = function(config){
15658     Roo.Component.call(this, config);
15659     this.addEvents({
15660         /**
15661          * @event resize
15662          * Fires after the component is resized.
15663              * @param {Roo.Component} this
15664              * @param {Number} adjWidth The box-adjusted width that was set
15665              * @param {Number} adjHeight The box-adjusted height that was set
15666              * @param {Number} rawWidth The width that was originally specified
15667              * @param {Number} rawHeight The height that was originally specified
15668              */
15669         resize : true,
15670         /**
15671          * @event move
15672          * Fires after the component is moved.
15673              * @param {Roo.Component} this
15674              * @param {Number} x The new x position
15675              * @param {Number} y The new y position
15676              */
15677         move : true
15678     });
15679 };
15680
15681 Roo.extend(Roo.BoxComponent, Roo.Component, {
15682     // private, set in afterRender to signify that the component has been rendered
15683     boxReady : false,
15684     // private, used to defer height settings to subclasses
15685     deferHeight: false,
15686     /** @cfg {Number} width
15687      * width (optional) size of component
15688      */
15689      /** @cfg {Number} height
15690      * height (optional) size of component
15691      */
15692      
15693     /**
15694      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15695      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15696      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15697      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15698      * @return {Roo.BoxComponent} this
15699      */
15700     setSize : function(w, h){
15701         // support for standard size objects
15702         if(typeof w == 'object'){
15703             h = w.height;
15704             w = w.width;
15705         }
15706         // not rendered
15707         if(!this.boxReady){
15708             this.width = w;
15709             this.height = h;
15710             return this;
15711         }
15712
15713         // prevent recalcs when not needed
15714         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15715             return this;
15716         }
15717         this.lastSize = {width: w, height: h};
15718
15719         var adj = this.adjustSize(w, h);
15720         var aw = adj.width, ah = adj.height;
15721         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15722             var rz = this.getResizeEl();
15723             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15724                 rz.setSize(aw, ah);
15725             }else if(!this.deferHeight && ah !== undefined){
15726                 rz.setHeight(ah);
15727             }else if(aw !== undefined){
15728                 rz.setWidth(aw);
15729             }
15730             this.onResize(aw, ah, w, h);
15731             this.fireEvent('resize', this, aw, ah, w, h);
15732         }
15733         return this;
15734     },
15735
15736     /**
15737      * Gets the current size of the component's underlying element.
15738      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15739      */
15740     getSize : function(){
15741         return this.el.getSize();
15742     },
15743
15744     /**
15745      * Gets the current XY position of the component's underlying element.
15746      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15747      * @return {Array} The XY position of the element (e.g., [100, 200])
15748      */
15749     getPosition : function(local){
15750         if(local === true){
15751             return [this.el.getLeft(true), this.el.getTop(true)];
15752         }
15753         return this.xy || this.el.getXY();
15754     },
15755
15756     /**
15757      * Gets the current box measurements of the component's underlying element.
15758      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15759      * @returns {Object} box An object in the format {x, y, width, height}
15760      */
15761     getBox : function(local){
15762         var s = this.el.getSize();
15763         if(local){
15764             s.x = this.el.getLeft(true);
15765             s.y = this.el.getTop(true);
15766         }else{
15767             var xy = this.xy || this.el.getXY();
15768             s.x = xy[0];
15769             s.y = xy[1];
15770         }
15771         return s;
15772     },
15773
15774     /**
15775      * Sets the current box measurements of the component's underlying element.
15776      * @param {Object} box An object in the format {x, y, width, height}
15777      * @returns {Roo.BoxComponent} this
15778      */
15779     updateBox : function(box){
15780         this.setSize(box.width, box.height);
15781         this.setPagePosition(box.x, box.y);
15782         return this;
15783     },
15784
15785     // protected
15786     getResizeEl : function(){
15787         return this.resizeEl || this.el;
15788     },
15789
15790     // protected
15791     getPositionEl : function(){
15792         return this.positionEl || this.el;
15793     },
15794
15795     /**
15796      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15797      * This method fires the move event.
15798      * @param {Number} left The new left
15799      * @param {Number} top The new top
15800      * @returns {Roo.BoxComponent} this
15801      */
15802     setPosition : function(x, y){
15803         this.x = x;
15804         this.y = y;
15805         if(!this.boxReady){
15806             return this;
15807         }
15808         var adj = this.adjustPosition(x, y);
15809         var ax = adj.x, ay = adj.y;
15810
15811         var el = this.getPositionEl();
15812         if(ax !== undefined || ay !== undefined){
15813             if(ax !== undefined && ay !== undefined){
15814                 el.setLeftTop(ax, ay);
15815             }else if(ax !== undefined){
15816                 el.setLeft(ax);
15817             }else if(ay !== undefined){
15818                 el.setTop(ay);
15819             }
15820             this.onPosition(ax, ay);
15821             this.fireEvent('move', this, ax, ay);
15822         }
15823         return this;
15824     },
15825
15826     /**
15827      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15828      * This method fires the move event.
15829      * @param {Number} x The new x position
15830      * @param {Number} y The new y position
15831      * @returns {Roo.BoxComponent} this
15832      */
15833     setPagePosition : function(x, y){
15834         this.pageX = x;
15835         this.pageY = y;
15836         if(!this.boxReady){
15837             return;
15838         }
15839         if(x === undefined || y === undefined){ // cannot translate undefined points
15840             return;
15841         }
15842         var p = this.el.translatePoints(x, y);
15843         this.setPosition(p.left, p.top);
15844         return this;
15845     },
15846
15847     // private
15848     onRender : function(ct, position){
15849         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15850         if(this.resizeEl){
15851             this.resizeEl = Roo.get(this.resizeEl);
15852         }
15853         if(this.positionEl){
15854             this.positionEl = Roo.get(this.positionEl);
15855         }
15856     },
15857
15858     // private
15859     afterRender : function(){
15860         Roo.BoxComponent.superclass.afterRender.call(this);
15861         this.boxReady = true;
15862         this.setSize(this.width, this.height);
15863         if(this.x || this.y){
15864             this.setPosition(this.x, this.y);
15865         }
15866         if(this.pageX || this.pageY){
15867             this.setPagePosition(this.pageX, this.pageY);
15868         }
15869     },
15870
15871     /**
15872      * Force the component's size to recalculate based on the underlying element's current height and width.
15873      * @returns {Roo.BoxComponent} this
15874      */
15875     syncSize : function(){
15876         delete this.lastSize;
15877         this.setSize(this.el.getWidth(), this.el.getHeight());
15878         return this;
15879     },
15880
15881     /**
15882      * Called after the component is resized, this method is empty by default but can be implemented by any
15883      * subclass that needs to perform custom logic after a resize occurs.
15884      * @param {Number} adjWidth The box-adjusted width that was set
15885      * @param {Number} adjHeight The box-adjusted height that was set
15886      * @param {Number} rawWidth The width that was originally specified
15887      * @param {Number} rawHeight The height that was originally specified
15888      */
15889     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15890
15891     },
15892
15893     /**
15894      * Called after the component is moved, this method is empty by default but can be implemented by any
15895      * subclass that needs to perform custom logic after a move occurs.
15896      * @param {Number} x The new x position
15897      * @param {Number} y The new y position
15898      */
15899     onPosition : function(x, y){
15900
15901     },
15902
15903     // private
15904     adjustSize : function(w, h){
15905         if(this.autoWidth){
15906             w = 'auto';
15907         }
15908         if(this.autoHeight){
15909             h = 'auto';
15910         }
15911         return {width : w, height: h};
15912     },
15913
15914     // private
15915     adjustPosition : function(x, y){
15916         return {x : x, y: y};
15917     }
15918 });/*
15919  * Original code for Roojs - LGPL
15920  * <script type="text/javascript">
15921  */
15922  
15923 /**
15924  * @class Roo.XComponent
15925  * A delayed Element creator...
15926  * Or a way to group chunks of interface together.
15927  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15928  *  used in conjunction with XComponent.build() it will create an instance of each element,
15929  *  then call addxtype() to build the User interface.
15930  * 
15931  * Mypart.xyx = new Roo.XComponent({
15932
15933     parent : 'Mypart.xyz', // empty == document.element.!!
15934     order : '001',
15935     name : 'xxxx'
15936     region : 'xxxx'
15937     disabled : function() {} 
15938      
15939     tree : function() { // return an tree of xtype declared components
15940         var MODULE = this;
15941         return 
15942         {
15943             xtype : 'NestedLayoutPanel',
15944             // technicall
15945         }
15946      ]
15947  *})
15948  *
15949  *
15950  * It can be used to build a big heiracy, with parent etc.
15951  * or you can just use this to render a single compoent to a dom element
15952  * MYPART.render(Roo.Element | String(id) | dom_element )
15953  *
15954  *
15955  * Usage patterns.
15956  *
15957  * Classic Roo
15958  *
15959  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15960  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15961  *
15962  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15963  *
15964  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15965  * - if mulitple topModules exist, the last one is defined as the top module.
15966  *
15967  * Embeded Roo
15968  * 
15969  * When the top level or multiple modules are to embedded into a existing HTML page,
15970  * the parent element can container '#id' of the element where the module will be drawn.
15971  *
15972  * Bootstrap Roo
15973  *
15974  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15975  * it relies more on a include mechanism, where sub modules are included into an outer page.
15976  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15977  * 
15978  * Bootstrap Roo Included elements
15979  *
15980  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15981  * hence confusing the component builder as it thinks there are multiple top level elements. 
15982  *
15983  * 
15984  * 
15985  * @extends Roo.util.Observable
15986  * @constructor
15987  * @param cfg {Object} configuration of component
15988  * 
15989  */
15990 Roo.XComponent = function(cfg) {
15991     Roo.apply(this, cfg);
15992     this.addEvents({ 
15993         /**
15994              * @event built
15995              * Fires when this the componnt is built
15996              * @param {Roo.XComponent} c the component
15997              */
15998         'built' : true
15999         
16000     });
16001     this.region = this.region || 'center'; // default..
16002     Roo.XComponent.register(this);
16003     this.modules = false;
16004     this.el = false; // where the layout goes..
16005     
16006     
16007 }
16008 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16009     /**
16010      * @property el
16011      * The created element (with Roo.factory())
16012      * @type {Roo.Layout}
16013      */
16014     el  : false,
16015     
16016     /**
16017      * @property el
16018      * for BC  - use el in new code
16019      * @type {Roo.Layout}
16020      */
16021     panel : false,
16022     
16023     /**
16024      * @property layout
16025      * for BC  - use el in new code
16026      * @type {Roo.Layout}
16027      */
16028     layout : false,
16029     
16030      /**
16031      * @cfg {Function|boolean} disabled
16032      * If this module is disabled by some rule, return true from the funtion
16033      */
16034     disabled : false,
16035     
16036     /**
16037      * @cfg {String} parent 
16038      * Name of parent element which it get xtype added to..
16039      */
16040     parent: false,
16041     
16042     /**
16043      * @cfg {String} order
16044      * Used to set the order in which elements are created (usefull for multiple tabs)
16045      */
16046     
16047     order : false,
16048     /**
16049      * @cfg {String} name
16050      * String to display while loading.
16051      */
16052     name : false,
16053     /**
16054      * @cfg {String} region
16055      * Region to render component to (defaults to center)
16056      */
16057     region : 'center',
16058     
16059     /**
16060      * @cfg {Array} items
16061      * A single item array - the first element is the root of the tree..
16062      * It's done this way to stay compatible with the Xtype system...
16063      */
16064     items : false,
16065     
16066     /**
16067      * @property _tree
16068      * The method that retuns the tree of parts that make up this compoennt 
16069      * @type {function}
16070      */
16071     _tree  : false,
16072     
16073      /**
16074      * render
16075      * render element to dom or tree
16076      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16077      */
16078     
16079     render : function(el)
16080     {
16081         
16082         el = el || false;
16083         var hp = this.parent ? 1 : 0;
16084         Roo.debug &&  Roo.log(this);
16085         
16086         var tree = this._tree ? this._tree() : this.tree();
16087
16088         
16089         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16090             // if parent is a '#.....' string, then let's use that..
16091             var ename = this.parent.substr(1);
16092             this.parent = false;
16093             Roo.debug && Roo.log(ename);
16094             switch (ename) {
16095                 case 'bootstrap-body':
16096                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16097                         // this is the BorderLayout standard?
16098                        this.parent = { el : true };
16099                        break;
16100                     }
16101                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16102                         // need to insert stuff...
16103                         this.parent =  {
16104                              el : new Roo.bootstrap.layout.Border({
16105                                  el : document.body, 
16106                      
16107                                  center: {
16108                                     titlebar: false,
16109                                     autoScroll:false,
16110                                     closeOnTab: true,
16111                                     tabPosition: 'top',
16112                                       //resizeTabs: true,
16113                                     alwaysShowTabs: true,
16114                                     hideTabs: false
16115                                      //minTabWidth: 140
16116                                  }
16117                              })
16118                         
16119                          };
16120                          break;
16121                     }
16122                          
16123                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16124                         this.parent = { el :  new  Roo.bootstrap.Body() };
16125                         Roo.debug && Roo.log("setting el to doc body");
16126                          
16127                     } else {
16128                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16129                     }
16130                     break;
16131                 case 'bootstrap':
16132                     this.parent = { el : true};
16133                     // fall through
16134                 default:
16135                     el = Roo.get(ename);
16136                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16137                         this.parent = { el : true};
16138                     }
16139                     
16140                     break;
16141             }
16142                 
16143             
16144             if (!el && !this.parent) {
16145                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16146                 return;
16147             }
16148         }
16149         
16150         Roo.debug && Roo.log("EL:");
16151         Roo.debug && Roo.log(el);
16152         Roo.debug && Roo.log("this.parent.el:");
16153         Roo.debug && Roo.log(this.parent.el);
16154         
16155
16156         // altertive root elements ??? - we need a better way to indicate these.
16157         var is_alt = Roo.XComponent.is_alt ||
16158                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16159                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16160                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16161         
16162         
16163         
16164         if (!this.parent && is_alt) {
16165             //el = Roo.get(document.body);
16166             this.parent = { el : true };
16167         }
16168             
16169             
16170         
16171         if (!this.parent) {
16172             
16173             Roo.debug && Roo.log("no parent - creating one");
16174             
16175             el = el ? Roo.get(el) : false;      
16176             
16177             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16178                 
16179                 this.parent =  {
16180                     el : new Roo.bootstrap.layout.Border({
16181                         el: el || document.body,
16182                     
16183                         center: {
16184                             titlebar: false,
16185                             autoScroll:false,
16186                             closeOnTab: true,
16187                             tabPosition: 'top',
16188                              //resizeTabs: true,
16189                             alwaysShowTabs: false,
16190                             hideTabs: true,
16191                             minTabWidth: 140,
16192                             overflow: 'visible'
16193                          }
16194                      })
16195                 };
16196             } else {
16197             
16198                 // it's a top level one..
16199                 this.parent =  {
16200                     el : new Roo.BorderLayout(el || document.body, {
16201                         center: {
16202                             titlebar: false,
16203                             autoScroll:false,
16204                             closeOnTab: true,
16205                             tabPosition: 'top',
16206                              //resizeTabs: true,
16207                             alwaysShowTabs: el && hp? false :  true,
16208                             hideTabs: el || !hp ? true :  false,
16209                             minTabWidth: 140
16210                          }
16211                     })
16212                 };
16213             }
16214         }
16215         
16216         if (!this.parent.el) {
16217                 // probably an old style ctor, which has been disabled.
16218                 return;
16219
16220         }
16221                 // The 'tree' method is  '_tree now' 
16222             
16223         tree.region = tree.region || this.region;
16224         var is_body = false;
16225         if (this.parent.el === true) {
16226             // bootstrap... - body..
16227             if (el) {
16228                 tree.el = el;
16229             }
16230             this.parent.el = Roo.factory(tree);
16231             is_body = true;
16232         }
16233         
16234         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16235         this.fireEvent('built', this);
16236         
16237         this.panel = this.el;
16238         this.layout = this.panel.layout;
16239         this.parentLayout = this.parent.layout  || false;  
16240          
16241     }
16242     
16243 });
16244
16245 Roo.apply(Roo.XComponent, {
16246     /**
16247      * @property  hideProgress
16248      * true to disable the building progress bar.. usefull on single page renders.
16249      * @type Boolean
16250      */
16251     hideProgress : false,
16252     /**
16253      * @property  buildCompleted
16254      * True when the builder has completed building the interface.
16255      * @type Boolean
16256      */
16257     buildCompleted : false,
16258      
16259     /**
16260      * @property  topModule
16261      * the upper most module - uses document.element as it's constructor.
16262      * @type Object
16263      */
16264      
16265     topModule  : false,
16266       
16267     /**
16268      * @property  modules
16269      * array of modules to be created by registration system.
16270      * @type {Array} of Roo.XComponent
16271      */
16272     
16273     modules : [],
16274     /**
16275      * @property  elmodules
16276      * array of modules to be created by which use #ID 
16277      * @type {Array} of Roo.XComponent
16278      */
16279      
16280     elmodules : [],
16281
16282      /**
16283      * @property  is_alt
16284      * Is an alternative Root - normally used by bootstrap or other systems,
16285      *    where the top element in the tree can wrap 'body' 
16286      * @type {boolean}  (default false)
16287      */
16288      
16289     is_alt : false,
16290     /**
16291      * @property  build_from_html
16292      * Build elements from html - used by bootstrap HTML stuff 
16293      *    - this is cleared after build is completed
16294      * @type {boolean}    (default false)
16295      */
16296      
16297     build_from_html : false,
16298     /**
16299      * Register components to be built later.
16300      *
16301      * This solves the following issues
16302      * - Building is not done on page load, but after an authentication process has occured.
16303      * - Interface elements are registered on page load
16304      * - Parent Interface elements may not be loaded before child, so this handles that..
16305      * 
16306      *
16307      * example:
16308      * 
16309      * MyApp.register({
16310           order : '000001',
16311           module : 'Pman.Tab.projectMgr',
16312           region : 'center',
16313           parent : 'Pman.layout',
16314           disabled : false,  // or use a function..
16315         })
16316      
16317      * * @param {Object} details about module
16318      */
16319     register : function(obj) {
16320                 
16321         Roo.XComponent.event.fireEvent('register', obj);
16322         switch(typeof(obj.disabled) ) {
16323                 
16324             case 'undefined':
16325                 break;
16326             
16327             case 'function':
16328                 if ( obj.disabled() ) {
16329                         return;
16330                 }
16331                 break;
16332             
16333             default:
16334                 if (obj.disabled) {
16335                         return;
16336                 }
16337                 break;
16338         }
16339                 
16340         this.modules.push(obj);
16341          
16342     },
16343     /**
16344      * convert a string to an object..
16345      * eg. 'AAA.BBB' -> finds AAA.BBB
16346
16347      */
16348     
16349     toObject : function(str)
16350     {
16351         if (!str || typeof(str) == 'object') {
16352             return str;
16353         }
16354         if (str.substring(0,1) == '#') {
16355             return str;
16356         }
16357
16358         var ar = str.split('.');
16359         var rt, o;
16360         rt = ar.shift();
16361             /** eval:var:o */
16362         try {
16363             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16364         } catch (e) {
16365             throw "Module not found : " + str;
16366         }
16367         
16368         if (o === false) {
16369             throw "Module not found : " + str;
16370         }
16371         Roo.each(ar, function(e) {
16372             if (typeof(o[e]) == 'undefined') {
16373                 throw "Module not found : " + str;
16374             }
16375             o = o[e];
16376         });
16377         
16378         return o;
16379         
16380     },
16381     
16382     
16383     /**
16384      * move modules into their correct place in the tree..
16385      * 
16386      */
16387     preBuild : function ()
16388     {
16389         var _t = this;
16390         Roo.each(this.modules , function (obj)
16391         {
16392             Roo.XComponent.event.fireEvent('beforebuild', obj);
16393             
16394             var opar = obj.parent;
16395             try { 
16396                 obj.parent = this.toObject(opar);
16397             } catch(e) {
16398                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16399                 return;
16400             }
16401             
16402             if (!obj.parent) {
16403                 Roo.debug && Roo.log("GOT top level module");
16404                 Roo.debug && Roo.log(obj);
16405                 obj.modules = new Roo.util.MixedCollection(false, 
16406                     function(o) { return o.order + '' }
16407                 );
16408                 this.topModule = obj;
16409                 return;
16410             }
16411                         // parent is a string (usually a dom element name..)
16412             if (typeof(obj.parent) == 'string') {
16413                 this.elmodules.push(obj);
16414                 return;
16415             }
16416             if (obj.parent.constructor != Roo.XComponent) {
16417                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16418             }
16419             if (!obj.parent.modules) {
16420                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16421                     function(o) { return o.order + '' }
16422                 );
16423             }
16424             if (obj.parent.disabled) {
16425                 obj.disabled = true;
16426             }
16427             obj.parent.modules.add(obj);
16428         }, this);
16429     },
16430     
16431      /**
16432      * make a list of modules to build.
16433      * @return {Array} list of modules. 
16434      */ 
16435     
16436     buildOrder : function()
16437     {
16438         var _this = this;
16439         var cmp = function(a,b) {   
16440             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16441         };
16442         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16443             throw "No top level modules to build";
16444         }
16445         
16446         // make a flat list in order of modules to build.
16447         var mods = this.topModule ? [ this.topModule ] : [];
16448                 
16449         
16450         // elmodules (is a list of DOM based modules )
16451         Roo.each(this.elmodules, function(e) {
16452             mods.push(e);
16453             if (!this.topModule &&
16454                 typeof(e.parent) == 'string' &&
16455                 e.parent.substring(0,1) == '#' &&
16456                 Roo.get(e.parent.substr(1))
16457                ) {
16458                 
16459                 _this.topModule = e;
16460             }
16461             
16462         });
16463
16464         
16465         // add modules to their parents..
16466         var addMod = function(m) {
16467             Roo.debug && Roo.log("build Order: add: " + m.name);
16468                 
16469             mods.push(m);
16470             if (m.modules && !m.disabled) {
16471                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16472                 m.modules.keySort('ASC',  cmp );
16473                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16474     
16475                 m.modules.each(addMod);
16476             } else {
16477                 Roo.debug && Roo.log("build Order: no child modules");
16478             }
16479             // not sure if this is used any more..
16480             if (m.finalize) {
16481                 m.finalize.name = m.name + " (clean up) ";
16482                 mods.push(m.finalize);
16483             }
16484             
16485         }
16486         if (this.topModule && this.topModule.modules) { 
16487             this.topModule.modules.keySort('ASC',  cmp );
16488             this.topModule.modules.each(addMod);
16489         } 
16490         return mods;
16491     },
16492     
16493      /**
16494      * Build the registered modules.
16495      * @param {Object} parent element.
16496      * @param {Function} optional method to call after module has been added.
16497      * 
16498      */ 
16499    
16500     build : function(opts) 
16501     {
16502         
16503         if (typeof(opts) != 'undefined') {
16504             Roo.apply(this,opts);
16505         }
16506         
16507         this.preBuild();
16508         var mods = this.buildOrder();
16509       
16510         //this.allmods = mods;
16511         //Roo.debug && Roo.log(mods);
16512         //return;
16513         if (!mods.length) { // should not happen
16514             throw "NO modules!!!";
16515         }
16516         
16517         
16518         var msg = "Building Interface...";
16519         // flash it up as modal - so we store the mask!?
16520         if (!this.hideProgress && Roo.MessageBox) {
16521             Roo.MessageBox.show({ title: 'loading' });
16522             Roo.MessageBox.show({
16523                title: "Please wait...",
16524                msg: msg,
16525                width:450,
16526                progress:true,
16527                closable:false,
16528                modal: false
16529               
16530             });
16531         }
16532         var total = mods.length;
16533         
16534         var _this = this;
16535         var progressRun = function() {
16536             if (!mods.length) {
16537                 Roo.debug && Roo.log('hide?');
16538                 if (!this.hideProgress && Roo.MessageBox) {
16539                     Roo.MessageBox.hide();
16540                 }
16541                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16542                 
16543                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16544                 
16545                 // THE END...
16546                 return false;   
16547             }
16548             
16549             var m = mods.shift();
16550             
16551             
16552             Roo.debug && Roo.log(m);
16553             // not sure if this is supported any more.. - modules that are are just function
16554             if (typeof(m) == 'function') { 
16555                 m.call(this);
16556                 return progressRun.defer(10, _this);
16557             } 
16558             
16559             
16560             msg = "Building Interface " + (total  - mods.length) + 
16561                     " of " + total + 
16562                     (m.name ? (' - ' + m.name) : '');
16563                         Roo.debug && Roo.log(msg);
16564             if (!_this.hideProgress &&  Roo.MessageBox) { 
16565                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16566             }
16567             
16568          
16569             // is the module disabled?
16570             var disabled = (typeof(m.disabled) == 'function') ?
16571                 m.disabled.call(m.module.disabled) : m.disabled;    
16572             
16573             
16574             if (disabled) {
16575                 return progressRun(); // we do not update the display!
16576             }
16577             
16578             // now build 
16579             
16580                         
16581                         
16582             m.render();
16583             // it's 10 on top level, and 1 on others??? why...
16584             return progressRun.defer(10, _this);
16585              
16586         }
16587         progressRun.defer(1, _this);
16588      
16589         
16590         
16591     },
16592         
16593         
16594         /**
16595          * Event Object.
16596          *
16597          *
16598          */
16599         event: false, 
16600     /**
16601          * wrapper for event.on - aliased later..  
16602          * Typically use to register a event handler for register:
16603          *
16604          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16605          *
16606          */
16607     on : false
16608    
16609     
16610     
16611 });
16612
16613 Roo.XComponent.event = new Roo.util.Observable({
16614                 events : { 
16615                         /**
16616                          * @event register
16617                          * Fires when an Component is registered,
16618                          * set the disable property on the Component to stop registration.
16619                          * @param {Roo.XComponent} c the component being registerd.
16620                          * 
16621                          */
16622                         'register' : true,
16623             /**
16624                          * @event beforebuild
16625                          * Fires before each Component is built
16626                          * can be used to apply permissions.
16627                          * @param {Roo.XComponent} c the component being registerd.
16628                          * 
16629                          */
16630                         'beforebuild' : true,
16631                         /**
16632                          * @event buildcomplete
16633                          * Fires on the top level element when all elements have been built
16634                          * @param {Roo.XComponent} the top level component.
16635                          */
16636                         'buildcomplete' : true
16637                         
16638                 }
16639 });
16640
16641 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16642  //
16643  /**
16644  * marked - a markdown parser
16645  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
16646  * https://github.com/chjj/marked
16647  */
16648
16649
16650 /**
16651  *
16652  * Roo.Markdown - is a very crude wrapper around marked..
16653  *
16654  * usage:
16655  * 
16656  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
16657  * 
16658  * Note: move the sample code to the bottom of this
16659  * file before uncommenting it.
16660  *
16661  */
16662
16663 Roo.Markdown = {};
16664 Roo.Markdown.toHtml = function(text) {
16665     
16666     var c = new Roo.Markdown.marked.setOptions({
16667             renderer: new Roo.Markdown.marked.Renderer(),
16668             gfm: true,
16669             tables: true,
16670             breaks: false,
16671             pedantic: false,
16672             sanitize: false,
16673             smartLists: true,
16674             smartypants: false
16675           });
16676     // A FEW HACKS!!?
16677     
16678     text = text.replace(/\\\n/g,' ');
16679     return Roo.Markdown.marked(text);
16680 };
16681 //
16682 // converter
16683 //
16684 // Wraps all "globals" so that the only thing
16685 // exposed is makeHtml().
16686 //
16687 (function() {
16688     
16689     /**
16690      * Block-Level Grammar
16691      */
16692     
16693     var block = {
16694       newline: /^\n+/,
16695       code: /^( {4}[^\n]+\n*)+/,
16696       fences: noop,
16697       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
16698       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
16699       nptable: noop,
16700       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
16701       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
16702       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
16703       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
16704       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
16705       table: noop,
16706       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
16707       text: /^[^\n]+/
16708     };
16709     
16710     block.bullet = /(?:[*+-]|\d+\.)/;
16711     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
16712     block.item = replace(block.item, 'gm')
16713       (/bull/g, block.bullet)
16714       ();
16715     
16716     block.list = replace(block.list)
16717       (/bull/g, block.bullet)
16718       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
16719       ('def', '\\n+(?=' + block.def.source + ')')
16720       ();
16721     
16722     block.blockquote = replace(block.blockquote)
16723       ('def', block.def)
16724       ();
16725     
16726     block._tag = '(?!(?:'
16727       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
16728       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
16729       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
16730     
16731     block.html = replace(block.html)
16732       ('comment', /<!--[\s\S]*?-->/)
16733       ('closed', /<(tag)[\s\S]+?<\/\1>/)
16734       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
16735       (/tag/g, block._tag)
16736       ();
16737     
16738     block.paragraph = replace(block.paragraph)
16739       ('hr', block.hr)
16740       ('heading', block.heading)
16741       ('lheading', block.lheading)
16742       ('blockquote', block.blockquote)
16743       ('tag', '<' + block._tag)
16744       ('def', block.def)
16745       ();
16746     
16747     /**
16748      * Normal Block Grammar
16749      */
16750     
16751     block.normal = merge({}, block);
16752     
16753     /**
16754      * GFM Block Grammar
16755      */
16756     
16757     block.gfm = merge({}, block.normal, {
16758       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
16759       paragraph: /^/,
16760       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
16761     });
16762     
16763     block.gfm.paragraph = replace(block.paragraph)
16764       ('(?!', '(?!'
16765         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
16766         + block.list.source.replace('\\1', '\\3') + '|')
16767       ();
16768     
16769     /**
16770      * GFM + Tables Block Grammar
16771      */
16772     
16773     block.tables = merge({}, block.gfm, {
16774       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
16775       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
16776     });
16777     
16778     /**
16779      * Block Lexer
16780      */
16781     
16782     function Lexer(options) {
16783       this.tokens = [];
16784       this.tokens.links = {};
16785       this.options = options || marked.defaults;
16786       this.rules = block.normal;
16787     
16788       if (this.options.gfm) {
16789         if (this.options.tables) {
16790           this.rules = block.tables;
16791         } else {
16792           this.rules = block.gfm;
16793         }
16794       }
16795     }
16796     
16797     /**
16798      * Expose Block Rules
16799      */
16800     
16801     Lexer.rules = block;
16802     
16803     /**
16804      * Static Lex Method
16805      */
16806     
16807     Lexer.lex = function(src, options) {
16808       var lexer = new Lexer(options);
16809       return lexer.lex(src);
16810     };
16811     
16812     /**
16813      * Preprocessing
16814      */
16815     
16816     Lexer.prototype.lex = function(src) {
16817       src = src
16818         .replace(/\r\n|\r/g, '\n')
16819         .replace(/\t/g, '    ')
16820         .replace(/\u00a0/g, ' ')
16821         .replace(/\u2424/g, '\n');
16822     
16823       return this.token(src, true);
16824     };
16825     
16826     /**
16827      * Lexing
16828      */
16829     
16830     Lexer.prototype.token = function(src, top, bq) {
16831       var src = src.replace(/^ +$/gm, '')
16832         , next
16833         , loose
16834         , cap
16835         , bull
16836         , b
16837         , item
16838         , space
16839         , i
16840         , l;
16841     
16842       while (src) {
16843         // newline
16844         if (cap = this.rules.newline.exec(src)) {
16845           src = src.substring(cap[0].length);
16846           if (cap[0].length > 1) {
16847             this.tokens.push({
16848               type: 'space'
16849             });
16850           }
16851         }
16852     
16853         // code
16854         if (cap = this.rules.code.exec(src)) {
16855           src = src.substring(cap[0].length);
16856           cap = cap[0].replace(/^ {4}/gm, '');
16857           this.tokens.push({
16858             type: 'code',
16859             text: !this.options.pedantic
16860               ? cap.replace(/\n+$/, '')
16861               : cap
16862           });
16863           continue;
16864         }
16865     
16866         // fences (gfm)
16867         if (cap = this.rules.fences.exec(src)) {
16868           src = src.substring(cap[0].length);
16869           this.tokens.push({
16870             type: 'code',
16871             lang: cap[2],
16872             text: cap[3] || ''
16873           });
16874           continue;
16875         }
16876     
16877         // heading
16878         if (cap = this.rules.heading.exec(src)) {
16879           src = src.substring(cap[0].length);
16880           this.tokens.push({
16881             type: 'heading',
16882             depth: cap[1].length,
16883             text: cap[2]
16884           });
16885           continue;
16886         }
16887     
16888         // table no leading pipe (gfm)
16889         if (top && (cap = this.rules.nptable.exec(src))) {
16890           src = src.substring(cap[0].length);
16891     
16892           item = {
16893             type: 'table',
16894             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
16895             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
16896             cells: cap[3].replace(/\n$/, '').split('\n')
16897           };
16898     
16899           for (i = 0; i < item.align.length; i++) {
16900             if (/^ *-+: *$/.test(item.align[i])) {
16901               item.align[i] = 'right';
16902             } else if (/^ *:-+: *$/.test(item.align[i])) {
16903               item.align[i] = 'center';
16904             } else if (/^ *:-+ *$/.test(item.align[i])) {
16905               item.align[i] = 'left';
16906             } else {
16907               item.align[i] = null;
16908             }
16909           }
16910     
16911           for (i = 0; i < item.cells.length; i++) {
16912             item.cells[i] = item.cells[i].split(/ *\| */);
16913           }
16914     
16915           this.tokens.push(item);
16916     
16917           continue;
16918         }
16919     
16920         // lheading
16921         if (cap = this.rules.lheading.exec(src)) {
16922           src = src.substring(cap[0].length);
16923           this.tokens.push({
16924             type: 'heading',
16925             depth: cap[2] === '=' ? 1 : 2,
16926             text: cap[1]
16927           });
16928           continue;
16929         }
16930     
16931         // hr
16932         if (cap = this.rules.hr.exec(src)) {
16933           src = src.substring(cap[0].length);
16934           this.tokens.push({
16935             type: 'hr'
16936           });
16937           continue;
16938         }
16939     
16940         // blockquote
16941         if (cap = this.rules.blockquote.exec(src)) {
16942           src = src.substring(cap[0].length);
16943     
16944           this.tokens.push({
16945             type: 'blockquote_start'
16946           });
16947     
16948           cap = cap[0].replace(/^ *> ?/gm, '');
16949     
16950           // Pass `top` to keep the current
16951           // "toplevel" state. This is exactly
16952           // how markdown.pl works.
16953           this.token(cap, top, true);
16954     
16955           this.tokens.push({
16956             type: 'blockquote_end'
16957           });
16958     
16959           continue;
16960         }
16961     
16962         // list
16963         if (cap = this.rules.list.exec(src)) {
16964           src = src.substring(cap[0].length);
16965           bull = cap[2];
16966     
16967           this.tokens.push({
16968             type: 'list_start',
16969             ordered: bull.length > 1
16970           });
16971     
16972           // Get each top-level item.
16973           cap = cap[0].match(this.rules.item);
16974     
16975           next = false;
16976           l = cap.length;
16977           i = 0;
16978     
16979           for (; i < l; i++) {
16980             item = cap[i];
16981     
16982             // Remove the list item's bullet
16983             // so it is seen as the next token.
16984             space = item.length;
16985             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
16986     
16987             // Outdent whatever the
16988             // list item contains. Hacky.
16989             if (~item.indexOf('\n ')) {
16990               space -= item.length;
16991               item = !this.options.pedantic
16992                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
16993                 : item.replace(/^ {1,4}/gm, '');
16994             }
16995     
16996             // Determine whether the next list item belongs here.
16997             // Backpedal if it does not belong in this list.
16998             if (this.options.smartLists && i !== l - 1) {
16999               b = block.bullet.exec(cap[i + 1])[0];
17000               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
17001                 src = cap.slice(i + 1).join('\n') + src;
17002                 i = l - 1;
17003               }
17004             }
17005     
17006             // Determine whether item is loose or not.
17007             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17008             // for discount behavior.
17009             loose = next || /\n\n(?!\s*$)/.test(item);
17010             if (i !== l - 1) {
17011               next = item.charAt(item.length - 1) === '\n';
17012               if (!loose) { loose = next; }
17013             }
17014     
17015             this.tokens.push({
17016               type: loose
17017                 ? 'loose_item_start'
17018                 : 'list_item_start'
17019             });
17020     
17021             // Recurse.
17022             this.token(item, false, bq);
17023     
17024             this.tokens.push({
17025               type: 'list_item_end'
17026             });
17027           }
17028     
17029           this.tokens.push({
17030             type: 'list_end'
17031           });
17032     
17033           continue;
17034         }
17035     
17036         // html
17037         if (cap = this.rules.html.exec(src)) {
17038           src = src.substring(cap[0].length);
17039           this.tokens.push({
17040             type: this.options.sanitize
17041               ? 'paragraph'
17042               : 'html',
17043             pre: !this.options.sanitizer
17044               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17045             text: cap[0]
17046           });
17047           continue;
17048         }
17049     
17050         // def
17051         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17052           src = src.substring(cap[0].length);
17053           this.tokens.links[cap[1].toLowerCase()] = {
17054             href: cap[2],
17055             title: cap[3]
17056           };
17057           continue;
17058         }
17059     
17060         // table (gfm)
17061         if (top && (cap = this.rules.table.exec(src))) {
17062           src = src.substring(cap[0].length);
17063     
17064           item = {
17065             type: 'table',
17066             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17067             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17068             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17069           };
17070     
17071           for (i = 0; i < item.align.length; i++) {
17072             if (/^ *-+: *$/.test(item.align[i])) {
17073               item.align[i] = 'right';
17074             } else if (/^ *:-+: *$/.test(item.align[i])) {
17075               item.align[i] = 'center';
17076             } else if (/^ *:-+ *$/.test(item.align[i])) {
17077               item.align[i] = 'left';
17078             } else {
17079               item.align[i] = null;
17080             }
17081           }
17082     
17083           for (i = 0; i < item.cells.length; i++) {
17084             item.cells[i] = item.cells[i]
17085               .replace(/^ *\| *| *\| *$/g, '')
17086               .split(/ *\| */);
17087           }
17088     
17089           this.tokens.push(item);
17090     
17091           continue;
17092         }
17093     
17094         // top-level paragraph
17095         if (top && (cap = this.rules.paragraph.exec(src))) {
17096           src = src.substring(cap[0].length);
17097           this.tokens.push({
17098             type: 'paragraph',
17099             text: cap[1].charAt(cap[1].length - 1) === '\n'
17100               ? cap[1].slice(0, -1)
17101               : cap[1]
17102           });
17103           continue;
17104         }
17105     
17106         // text
17107         if (cap = this.rules.text.exec(src)) {
17108           // Top-level should never reach here.
17109           src = src.substring(cap[0].length);
17110           this.tokens.push({
17111             type: 'text',
17112             text: cap[0]
17113           });
17114           continue;
17115         }
17116     
17117         if (src) {
17118           throw new
17119             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17120         }
17121       }
17122     
17123       return this.tokens;
17124     };
17125     
17126     /**
17127      * Inline-Level Grammar
17128      */
17129     
17130     var inline = {
17131       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17132       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17133       url: noop,
17134       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17135       link: /^!?\[(inside)\]\(href\)/,
17136       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17137       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17138       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17139       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17140       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17141       br: /^ {2,}\n(?!\s*$)/,
17142       del: noop,
17143       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17144     };
17145     
17146     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17147     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17148     
17149     inline.link = replace(inline.link)
17150       ('inside', inline._inside)
17151       ('href', inline._href)
17152       ();
17153     
17154     inline.reflink = replace(inline.reflink)
17155       ('inside', inline._inside)
17156       ();
17157     
17158     /**
17159      * Normal Inline Grammar
17160      */
17161     
17162     inline.normal = merge({}, inline);
17163     
17164     /**
17165      * Pedantic Inline Grammar
17166      */
17167     
17168     inline.pedantic = merge({}, inline.normal, {
17169       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17170       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17171     });
17172     
17173     /**
17174      * GFM Inline Grammar
17175      */
17176     
17177     inline.gfm = merge({}, inline.normal, {
17178       escape: replace(inline.escape)('])', '~|])')(),
17179       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17180       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17181       text: replace(inline.text)
17182         (']|', '~]|')
17183         ('|', '|https?://|')
17184         ()
17185     });
17186     
17187     /**
17188      * GFM + Line Breaks Inline Grammar
17189      */
17190     
17191     inline.breaks = merge({}, inline.gfm, {
17192       br: replace(inline.br)('{2,}', '*')(),
17193       text: replace(inline.gfm.text)('{2,}', '*')()
17194     });
17195     
17196     /**
17197      * Inline Lexer & Compiler
17198      */
17199     
17200     function InlineLexer(links, options) {
17201       this.options = options || marked.defaults;
17202       this.links = links;
17203       this.rules = inline.normal;
17204       this.renderer = this.options.renderer || new Renderer;
17205       this.renderer.options = this.options;
17206     
17207       if (!this.links) {
17208         throw new
17209           Error('Tokens array requires a `links` property.');
17210       }
17211     
17212       if (this.options.gfm) {
17213         if (this.options.breaks) {
17214           this.rules = inline.breaks;
17215         } else {
17216           this.rules = inline.gfm;
17217         }
17218       } else if (this.options.pedantic) {
17219         this.rules = inline.pedantic;
17220       }
17221     }
17222     
17223     /**
17224      * Expose Inline Rules
17225      */
17226     
17227     InlineLexer.rules = inline;
17228     
17229     /**
17230      * Static Lexing/Compiling Method
17231      */
17232     
17233     InlineLexer.output = function(src, links, options) {
17234       var inline = new InlineLexer(links, options);
17235       return inline.output(src);
17236     };
17237     
17238     /**
17239      * Lexing/Compiling
17240      */
17241     
17242     InlineLexer.prototype.output = function(src) {
17243       var out = ''
17244         , link
17245         , text
17246         , href
17247         , cap;
17248     
17249       while (src) {
17250         // escape
17251         if (cap = this.rules.escape.exec(src)) {
17252           src = src.substring(cap[0].length);
17253           out += cap[1];
17254           continue;
17255         }
17256     
17257         // autolink
17258         if (cap = this.rules.autolink.exec(src)) {
17259           src = src.substring(cap[0].length);
17260           if (cap[2] === '@') {
17261             text = cap[1].charAt(6) === ':'
17262               ? this.mangle(cap[1].substring(7))
17263               : this.mangle(cap[1]);
17264             href = this.mangle('mailto:') + text;
17265           } else {
17266             text = escape(cap[1]);
17267             href = text;
17268           }
17269           out += this.renderer.link(href, null, text);
17270           continue;
17271         }
17272     
17273         // url (gfm)
17274         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17275           src = src.substring(cap[0].length);
17276           text = escape(cap[1]);
17277           href = text;
17278           out += this.renderer.link(href, null, text);
17279           continue;
17280         }
17281     
17282         // tag
17283         if (cap = this.rules.tag.exec(src)) {
17284           if (!this.inLink && /^<a /i.test(cap[0])) {
17285             this.inLink = true;
17286           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17287             this.inLink = false;
17288           }
17289           src = src.substring(cap[0].length);
17290           out += this.options.sanitize
17291             ? this.options.sanitizer
17292               ? this.options.sanitizer(cap[0])
17293               : escape(cap[0])
17294             : cap[0];
17295           continue;
17296         }
17297     
17298         // link
17299         if (cap = this.rules.link.exec(src)) {
17300           src = src.substring(cap[0].length);
17301           this.inLink = true;
17302           out += this.outputLink(cap, {
17303             href: cap[2],
17304             title: cap[3]
17305           });
17306           this.inLink = false;
17307           continue;
17308         }
17309     
17310         // reflink, nolink
17311         if ((cap = this.rules.reflink.exec(src))
17312             || (cap = this.rules.nolink.exec(src))) {
17313           src = src.substring(cap[0].length);
17314           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
17315           link = this.links[link.toLowerCase()];
17316           if (!link || !link.href) {
17317             out += cap[0].charAt(0);
17318             src = cap[0].substring(1) + src;
17319             continue;
17320           }
17321           this.inLink = true;
17322           out += this.outputLink(cap, link);
17323           this.inLink = false;
17324           continue;
17325         }
17326     
17327         // strong
17328         if (cap = this.rules.strong.exec(src)) {
17329           src = src.substring(cap[0].length);
17330           out += this.renderer.strong(this.output(cap[2] || cap[1]));
17331           continue;
17332         }
17333     
17334         // em
17335         if (cap = this.rules.em.exec(src)) {
17336           src = src.substring(cap[0].length);
17337           out += this.renderer.em(this.output(cap[2] || cap[1]));
17338           continue;
17339         }
17340     
17341         // code
17342         if (cap = this.rules.code.exec(src)) {
17343           src = src.substring(cap[0].length);
17344           out += this.renderer.codespan(escape(cap[2], true));
17345           continue;
17346         }
17347     
17348         // br
17349         if (cap = this.rules.br.exec(src)) {
17350           src = src.substring(cap[0].length);
17351           out += this.renderer.br();
17352           continue;
17353         }
17354     
17355         // del (gfm)
17356         if (cap = this.rules.del.exec(src)) {
17357           src = src.substring(cap[0].length);
17358           out += this.renderer.del(this.output(cap[1]));
17359           continue;
17360         }
17361     
17362         // text
17363         if (cap = this.rules.text.exec(src)) {
17364           src = src.substring(cap[0].length);
17365           out += this.renderer.text(escape(this.smartypants(cap[0])));
17366           continue;
17367         }
17368     
17369         if (src) {
17370           throw new
17371             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17372         }
17373       }
17374     
17375       return out;
17376     };
17377     
17378     /**
17379      * Compile Link
17380      */
17381     
17382     InlineLexer.prototype.outputLink = function(cap, link) {
17383       var href = escape(link.href)
17384         , title = link.title ? escape(link.title) : null;
17385     
17386       return cap[0].charAt(0) !== '!'
17387         ? this.renderer.link(href, title, this.output(cap[1]))
17388         : this.renderer.image(href, title, escape(cap[1]));
17389     };
17390     
17391     /**
17392      * Smartypants Transformations
17393      */
17394     
17395     InlineLexer.prototype.smartypants = function(text) {
17396       if (!this.options.smartypants)  { return text; }
17397       return text
17398         // em-dashes
17399         .replace(/---/g, '\u2014')
17400         // en-dashes
17401         .replace(/--/g, '\u2013')
17402         // opening singles
17403         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
17404         // closing singles & apostrophes
17405         .replace(/'/g, '\u2019')
17406         // opening doubles
17407         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
17408         // closing doubles
17409         .replace(/"/g, '\u201d')
17410         // ellipses
17411         .replace(/\.{3}/g, '\u2026');
17412     };
17413     
17414     /**
17415      * Mangle Links
17416      */
17417     
17418     InlineLexer.prototype.mangle = function(text) {
17419       if (!this.options.mangle) { return text; }
17420       var out = ''
17421         , l = text.length
17422         , i = 0
17423         , ch;
17424     
17425       for (; i < l; i++) {
17426         ch = text.charCodeAt(i);
17427         if (Math.random() > 0.5) {
17428           ch = 'x' + ch.toString(16);
17429         }
17430         out += '&#' + ch + ';';
17431       }
17432     
17433       return out;
17434     };
17435     
17436     /**
17437      * Renderer
17438      */
17439     
17440     function Renderer(options) {
17441       this.options = options || {};
17442     }
17443     
17444     Renderer.prototype.code = function(code, lang, escaped) {
17445       if (this.options.highlight) {
17446         var out = this.options.highlight(code, lang);
17447         if (out != null && out !== code) {
17448           escaped = true;
17449           code = out;
17450         }
17451       } else {
17452             // hack!!! - it's already escapeD?
17453             escaped = true;
17454       }
17455     
17456       if (!lang) {
17457         return '<pre><code>'
17458           + (escaped ? code : escape(code, true))
17459           + '\n</code></pre>';
17460       }
17461     
17462       return '<pre><code class="'
17463         + this.options.langPrefix
17464         + escape(lang, true)
17465         + '">'
17466         + (escaped ? code : escape(code, true))
17467         + '\n</code></pre>\n';
17468     };
17469     
17470     Renderer.prototype.blockquote = function(quote) {
17471       return '<blockquote>\n' + quote + '</blockquote>\n';
17472     };
17473     
17474     Renderer.prototype.html = function(html) {
17475       return html;
17476     };
17477     
17478     Renderer.prototype.heading = function(text, level, raw) {
17479       return '<h'
17480         + level
17481         + ' id="'
17482         + this.options.headerPrefix
17483         + raw.toLowerCase().replace(/[^\w]+/g, '-')
17484         + '">'
17485         + text
17486         + '</h'
17487         + level
17488         + '>\n';
17489     };
17490     
17491     Renderer.prototype.hr = function() {
17492       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
17493     };
17494     
17495     Renderer.prototype.list = function(body, ordered) {
17496       var type = ordered ? 'ol' : 'ul';
17497       return '<' + type + '>\n' + body + '</' + type + '>\n';
17498     };
17499     
17500     Renderer.prototype.listitem = function(text) {
17501       return '<li>' + text + '</li>\n';
17502     };
17503     
17504     Renderer.prototype.paragraph = function(text) {
17505       return '<p>' + text + '</p>\n';
17506     };
17507     
17508     Renderer.prototype.table = function(header, body) {
17509       return '<table class="table table-striped">\n'
17510         + '<thead>\n'
17511         + header
17512         + '</thead>\n'
17513         + '<tbody>\n'
17514         + body
17515         + '</tbody>\n'
17516         + '</table>\n';
17517     };
17518     
17519     Renderer.prototype.tablerow = function(content) {
17520       return '<tr>\n' + content + '</tr>\n';
17521     };
17522     
17523     Renderer.prototype.tablecell = function(content, flags) {
17524       var type = flags.header ? 'th' : 'td';
17525       var tag = flags.align
17526         ? '<' + type + ' style="text-align:' + flags.align + '">'
17527         : '<' + type + '>';
17528       return tag + content + '</' + type + '>\n';
17529     };
17530     
17531     // span level renderer
17532     Renderer.prototype.strong = function(text) {
17533       return '<strong>' + text + '</strong>';
17534     };
17535     
17536     Renderer.prototype.em = function(text) {
17537       return '<em>' + text + '</em>';
17538     };
17539     
17540     Renderer.prototype.codespan = function(text) {
17541       return '<code>' + text + '</code>';
17542     };
17543     
17544     Renderer.prototype.br = function() {
17545       return this.options.xhtml ? '<br/>' : '<br>';
17546     };
17547     
17548     Renderer.prototype.del = function(text) {
17549       return '<del>' + text + '</del>';
17550     };
17551     
17552     Renderer.prototype.link = function(href, title, text) {
17553       if (this.options.sanitize) {
17554         try {
17555           var prot = decodeURIComponent(unescape(href))
17556             .replace(/[^\w:]/g, '')
17557             .toLowerCase();
17558         } catch (e) {
17559           return '';
17560         }
17561         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
17562           return '';
17563         }
17564       }
17565       var out = '<a href="' + href + '"';
17566       if (title) {
17567         out += ' title="' + title + '"';
17568       }
17569       out += '>' + text + '</a>';
17570       return out;
17571     };
17572     
17573     Renderer.prototype.image = function(href, title, text) {
17574       var out = '<img src="' + href + '" alt="' + text + '"';
17575       if (title) {
17576         out += ' title="' + title + '"';
17577       }
17578       out += this.options.xhtml ? '/>' : '>';
17579       return out;
17580     };
17581     
17582     Renderer.prototype.text = function(text) {
17583       return text;
17584     };
17585     
17586     /**
17587      * Parsing & Compiling
17588      */
17589     
17590     function Parser(options) {
17591       this.tokens = [];
17592       this.token = null;
17593       this.options = options || marked.defaults;
17594       this.options.renderer = this.options.renderer || new Renderer;
17595       this.renderer = this.options.renderer;
17596       this.renderer.options = this.options;
17597     }
17598     
17599     /**
17600      * Static Parse Method
17601      */
17602     
17603     Parser.parse = function(src, options, renderer) {
17604       var parser = new Parser(options, renderer);
17605       return parser.parse(src);
17606     };
17607     
17608     /**
17609      * Parse Loop
17610      */
17611     
17612     Parser.prototype.parse = function(src) {
17613       this.inline = new InlineLexer(src.links, this.options, this.renderer);
17614       this.tokens = src.reverse();
17615     
17616       var out = '';
17617       while (this.next()) {
17618         out += this.tok();
17619       }
17620     
17621       return out;
17622     };
17623     
17624     /**
17625      * Next Token
17626      */
17627     
17628     Parser.prototype.next = function() {
17629       return this.token = this.tokens.pop();
17630     };
17631     
17632     /**
17633      * Preview Next Token
17634      */
17635     
17636     Parser.prototype.peek = function() {
17637       return this.tokens[this.tokens.length - 1] || 0;
17638     };
17639     
17640     /**
17641      * Parse Text Tokens
17642      */
17643     
17644     Parser.prototype.parseText = function() {
17645       var body = this.token.text;
17646     
17647       while (this.peek().type === 'text') {
17648         body += '\n' + this.next().text;
17649       }
17650     
17651       return this.inline.output(body);
17652     };
17653     
17654     /**
17655      * Parse Current Token
17656      */
17657     
17658     Parser.prototype.tok = function() {
17659       switch (this.token.type) {
17660         case 'space': {
17661           return '';
17662         }
17663         case 'hr': {
17664           return this.renderer.hr();
17665         }
17666         case 'heading': {
17667           return this.renderer.heading(
17668             this.inline.output(this.token.text),
17669             this.token.depth,
17670             this.token.text);
17671         }
17672         case 'code': {
17673           return this.renderer.code(this.token.text,
17674             this.token.lang,
17675             this.token.escaped);
17676         }
17677         case 'table': {
17678           var header = ''
17679             , body = ''
17680             , i
17681             , row
17682             , cell
17683             , flags
17684             , j;
17685     
17686           // header
17687           cell = '';
17688           for (i = 0; i < this.token.header.length; i++) {
17689             flags = { header: true, align: this.token.align[i] };
17690             cell += this.renderer.tablecell(
17691               this.inline.output(this.token.header[i]),
17692               { header: true, align: this.token.align[i] }
17693             );
17694           }
17695           header += this.renderer.tablerow(cell);
17696     
17697           for (i = 0; i < this.token.cells.length; i++) {
17698             row = this.token.cells[i];
17699     
17700             cell = '';
17701             for (j = 0; j < row.length; j++) {
17702               cell += this.renderer.tablecell(
17703                 this.inline.output(row[j]),
17704                 { header: false, align: this.token.align[j] }
17705               );
17706             }
17707     
17708             body += this.renderer.tablerow(cell);
17709           }
17710           return this.renderer.table(header, body);
17711         }
17712         case 'blockquote_start': {
17713           var body = '';
17714     
17715           while (this.next().type !== 'blockquote_end') {
17716             body += this.tok();
17717           }
17718     
17719           return this.renderer.blockquote(body);
17720         }
17721         case 'list_start': {
17722           var body = ''
17723             , ordered = this.token.ordered;
17724     
17725           while (this.next().type !== 'list_end') {
17726             body += this.tok();
17727           }
17728     
17729           return this.renderer.list(body, ordered);
17730         }
17731         case 'list_item_start': {
17732           var body = '';
17733     
17734           while (this.next().type !== 'list_item_end') {
17735             body += this.token.type === 'text'
17736               ? this.parseText()
17737               : this.tok();
17738           }
17739     
17740           return this.renderer.listitem(body);
17741         }
17742         case 'loose_item_start': {
17743           var body = '';
17744     
17745           while (this.next().type !== 'list_item_end') {
17746             body += this.tok();
17747           }
17748     
17749           return this.renderer.listitem(body);
17750         }
17751         case 'html': {
17752           var html = !this.token.pre && !this.options.pedantic
17753             ? this.inline.output(this.token.text)
17754             : this.token.text;
17755           return this.renderer.html(html);
17756         }
17757         case 'paragraph': {
17758           return this.renderer.paragraph(this.inline.output(this.token.text));
17759         }
17760         case 'text': {
17761           return this.renderer.paragraph(this.parseText());
17762         }
17763       }
17764     };
17765     
17766     /**
17767      * Helpers
17768      */
17769     
17770     function escape(html, encode) {
17771       return html
17772         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17773         .replace(/</g, '&lt;')
17774         .replace(/>/g, '&gt;')
17775         .replace(/"/g, '&quot;')
17776         .replace(/'/g, '&#39;');
17777     }
17778     
17779     function unescape(html) {
17780         // explicitly match decimal, hex, and named HTML entities 
17781       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17782         n = n.toLowerCase();
17783         if (n === 'colon') { return ':'; }
17784         if (n.charAt(0) === '#') {
17785           return n.charAt(1) === 'x'
17786             ? String.fromCharCode(parseInt(n.substring(2), 16))
17787             : String.fromCharCode(+n.substring(1));
17788         }
17789         return '';
17790       });
17791     }
17792     
17793     function replace(regex, opt) {
17794       regex = regex.source;
17795       opt = opt || '';
17796       return function self(name, val) {
17797         if (!name) { return new RegExp(regex, opt); }
17798         val = val.source || val;
17799         val = val.replace(/(^|[^\[])\^/g, '$1');
17800         regex = regex.replace(name, val);
17801         return self;
17802       };
17803     }
17804     
17805     function noop() {}
17806     noop.exec = noop;
17807     
17808     function merge(obj) {
17809       var i = 1
17810         , target
17811         , key;
17812     
17813       for (; i < arguments.length; i++) {
17814         target = arguments[i];
17815         for (key in target) {
17816           if (Object.prototype.hasOwnProperty.call(target, key)) {
17817             obj[key] = target[key];
17818           }
17819         }
17820       }
17821     
17822       return obj;
17823     }
17824     
17825     
17826     /**
17827      * Marked
17828      */
17829     
17830     function marked(src, opt, callback) {
17831       if (callback || typeof opt === 'function') {
17832         if (!callback) {
17833           callback = opt;
17834           opt = null;
17835         }
17836     
17837         opt = merge({}, marked.defaults, opt || {});
17838     
17839         var highlight = opt.highlight
17840           , tokens
17841           , pending
17842           , i = 0;
17843     
17844         try {
17845           tokens = Lexer.lex(src, opt)
17846         } catch (e) {
17847           return callback(e);
17848         }
17849     
17850         pending = tokens.length;
17851     
17852         var done = function(err) {
17853           if (err) {
17854             opt.highlight = highlight;
17855             return callback(err);
17856           }
17857     
17858           var out;
17859     
17860           try {
17861             out = Parser.parse(tokens, opt);
17862           } catch (e) {
17863             err = e;
17864           }
17865     
17866           opt.highlight = highlight;
17867     
17868           return err
17869             ? callback(err)
17870             : callback(null, out);
17871         };
17872     
17873         if (!highlight || highlight.length < 3) {
17874           return done();
17875         }
17876     
17877         delete opt.highlight;
17878     
17879         if (!pending) { return done(); }
17880     
17881         for (; i < tokens.length; i++) {
17882           (function(token) {
17883             if (token.type !== 'code') {
17884               return --pending || done();
17885             }
17886             return highlight(token.text, token.lang, function(err, code) {
17887               if (err) { return done(err); }
17888               if (code == null || code === token.text) {
17889                 return --pending || done();
17890               }
17891               token.text = code;
17892               token.escaped = true;
17893               --pending || done();
17894             });
17895           })(tokens[i]);
17896         }
17897     
17898         return;
17899       }
17900       try {
17901         if (opt) { opt = merge({}, marked.defaults, opt); }
17902         return Parser.parse(Lexer.lex(src, opt), opt);
17903       } catch (e) {
17904         e.message += '\nPlease report this to https://github.com/chjj/marked.';
17905         if ((opt || marked.defaults).silent) {
17906           return '<p>An error occured:</p><pre>'
17907             + escape(e.message + '', true)
17908             + '</pre>';
17909         }
17910         throw e;
17911       }
17912     }
17913     
17914     /**
17915      * Options
17916      */
17917     
17918     marked.options =
17919     marked.setOptions = function(opt) {
17920       merge(marked.defaults, opt);
17921       return marked;
17922     };
17923     
17924     marked.defaults = {
17925       gfm: true,
17926       tables: true,
17927       breaks: false,
17928       pedantic: false,
17929       sanitize: false,
17930       sanitizer: null,
17931       mangle: true,
17932       smartLists: false,
17933       silent: false,
17934       highlight: null,
17935       langPrefix: 'lang-',
17936       smartypants: false,
17937       headerPrefix: '',
17938       renderer: new Renderer,
17939       xhtml: false
17940     };
17941     
17942     /**
17943      * Expose
17944      */
17945     
17946     marked.Parser = Parser;
17947     marked.parser = Parser.parse;
17948     
17949     marked.Renderer = Renderer;
17950     
17951     marked.Lexer = Lexer;
17952     marked.lexer = Lexer.lex;
17953     
17954     marked.InlineLexer = InlineLexer;
17955     marked.inlineLexer = InlineLexer.output;
17956     
17957     marked.parse = marked;
17958     
17959     Roo.Markdown.marked = marked;
17960
17961 })();/*
17962  * Based on:
17963  * Ext JS Library 1.1.1
17964  * Copyright(c) 2006-2007, Ext JS, LLC.
17965  *
17966  * Originally Released Under LGPL - original licence link has changed is not relivant.
17967  *
17968  * Fork - LGPL
17969  * <script type="text/javascript">
17970  */
17971
17972
17973
17974 /*
17975  * These classes are derivatives of the similarly named classes in the YUI Library.
17976  * The original license:
17977  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
17978  * Code licensed under the BSD License:
17979  * http://developer.yahoo.net/yui/license.txt
17980  */
17981
17982 (function() {
17983
17984 var Event=Roo.EventManager;
17985 var Dom=Roo.lib.Dom;
17986
17987 /**
17988  * @class Roo.dd.DragDrop
17989  * @extends Roo.util.Observable
17990  * Defines the interface and base operation of items that that can be
17991  * dragged or can be drop targets.  It was designed to be extended, overriding
17992  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
17993  * Up to three html elements can be associated with a DragDrop instance:
17994  * <ul>
17995  * <li>linked element: the element that is passed into the constructor.
17996  * This is the element which defines the boundaries for interaction with
17997  * other DragDrop objects.</li>
17998  * <li>handle element(s): The drag operation only occurs if the element that
17999  * was clicked matches a handle element.  By default this is the linked
18000  * element, but there are times that you will want only a portion of the
18001  * linked element to initiate the drag operation, and the setHandleElId()
18002  * method provides a way to define this.</li>
18003  * <li>drag element: this represents the element that would be moved along
18004  * with the cursor during a drag operation.  By default, this is the linked
18005  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18006  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18007  * </li>
18008  * </ul>
18009  * This class should not be instantiated until the onload event to ensure that
18010  * the associated elements are available.
18011  * The following would define a DragDrop obj that would interact with any
18012  * other DragDrop obj in the "group1" group:
18013  * <pre>
18014  *  dd = new Roo.dd.DragDrop("div1", "group1");
18015  * </pre>
18016  * Since none of the event handlers have been implemented, nothing would
18017  * actually happen if you were to run the code above.  Normally you would
18018  * override this class or one of the default implementations, but you can
18019  * also override the methods you want on an instance of the class...
18020  * <pre>
18021  *  dd.onDragDrop = function(e, id) {
18022  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18023  *  }
18024  * </pre>
18025  * @constructor
18026  * @param {String} id of the element that is linked to this instance
18027  * @param {String} sGroup the group of related DragDrop objects
18028  * @param {object} config an object containing configurable attributes
18029  *                Valid properties for DragDrop:
18030  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18031  */
18032 Roo.dd.DragDrop = function(id, sGroup, config) {
18033     if (id) {
18034         this.init(id, sGroup, config);
18035     }
18036     
18037 };
18038
18039 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18040
18041     /**
18042      * The id of the element associated with this object.  This is what we
18043      * refer to as the "linked element" because the size and position of
18044      * this element is used to determine when the drag and drop objects have
18045      * interacted.
18046      * @property id
18047      * @type String
18048      */
18049     id: null,
18050
18051     /**
18052      * Configuration attributes passed into the constructor
18053      * @property config
18054      * @type object
18055      */
18056     config: null,
18057
18058     /**
18059      * The id of the element that will be dragged.  By default this is same
18060      * as the linked element , but could be changed to another element. Ex:
18061      * Roo.dd.DDProxy
18062      * @property dragElId
18063      * @type String
18064      * @private
18065      */
18066     dragElId: null,
18067
18068     /**
18069      * the id of the element that initiates the drag operation.  By default
18070      * this is the linked element, but could be changed to be a child of this
18071      * element.  This lets us do things like only starting the drag when the
18072      * header element within the linked html element is clicked.
18073      * @property handleElId
18074      * @type String
18075      * @private
18076      */
18077     handleElId: null,
18078
18079     /**
18080      * An associative array of HTML tags that will be ignored if clicked.
18081      * @property invalidHandleTypes
18082      * @type {string: string}
18083      */
18084     invalidHandleTypes: null,
18085
18086     /**
18087      * An associative array of ids for elements that will be ignored if clicked
18088      * @property invalidHandleIds
18089      * @type {string: string}
18090      */
18091     invalidHandleIds: null,
18092
18093     /**
18094      * An indexted array of css class names for elements that will be ignored
18095      * if clicked.
18096      * @property invalidHandleClasses
18097      * @type string[]
18098      */
18099     invalidHandleClasses: null,
18100
18101     /**
18102      * The linked element's absolute X position at the time the drag was
18103      * started
18104      * @property startPageX
18105      * @type int
18106      * @private
18107      */
18108     startPageX: 0,
18109
18110     /**
18111      * The linked element's absolute X position at the time the drag was
18112      * started
18113      * @property startPageY
18114      * @type int
18115      * @private
18116      */
18117     startPageY: 0,
18118
18119     /**
18120      * The group defines a logical collection of DragDrop objects that are
18121      * related.  Instances only get events when interacting with other
18122      * DragDrop object in the same group.  This lets us define multiple
18123      * groups using a single DragDrop subclass if we want.
18124      * @property groups
18125      * @type {string: string}
18126      */
18127     groups: null,
18128
18129     /**
18130      * Individual drag/drop instances can be locked.  This will prevent
18131      * onmousedown start drag.
18132      * @property locked
18133      * @type boolean
18134      * @private
18135      */
18136     locked: false,
18137
18138     /**
18139      * Lock this instance
18140      * @method lock
18141      */
18142     lock: function() { this.locked = true; },
18143
18144     /**
18145      * Unlock this instace
18146      * @method unlock
18147      */
18148     unlock: function() { this.locked = false; },
18149
18150     /**
18151      * By default, all insances can be a drop target.  This can be disabled by
18152      * setting isTarget to false.
18153      * @method isTarget
18154      * @type boolean
18155      */
18156     isTarget: true,
18157
18158     /**
18159      * The padding configured for this drag and drop object for calculating
18160      * the drop zone intersection with this object.
18161      * @method padding
18162      * @type int[]
18163      */
18164     padding: null,
18165
18166     /**
18167      * Cached reference to the linked element
18168      * @property _domRef
18169      * @private
18170      */
18171     _domRef: null,
18172
18173     /**
18174      * Internal typeof flag
18175      * @property __ygDragDrop
18176      * @private
18177      */
18178     __ygDragDrop: true,
18179
18180     /**
18181      * Set to true when horizontal contraints are applied
18182      * @property constrainX
18183      * @type boolean
18184      * @private
18185      */
18186     constrainX: false,
18187
18188     /**
18189      * Set to true when vertical contraints are applied
18190      * @property constrainY
18191      * @type boolean
18192      * @private
18193      */
18194     constrainY: false,
18195
18196     /**
18197      * The left constraint
18198      * @property minX
18199      * @type int
18200      * @private
18201      */
18202     minX: 0,
18203
18204     /**
18205      * The right constraint
18206      * @property maxX
18207      * @type int
18208      * @private
18209      */
18210     maxX: 0,
18211
18212     /**
18213      * The up constraint
18214      * @property minY
18215      * @type int
18216      * @type int
18217      * @private
18218      */
18219     minY: 0,
18220
18221     /**
18222      * The down constraint
18223      * @property maxY
18224      * @type int
18225      * @private
18226      */
18227     maxY: 0,
18228
18229     /**
18230      * Maintain offsets when we resetconstraints.  Set to true when you want
18231      * the position of the element relative to its parent to stay the same
18232      * when the page changes
18233      *
18234      * @property maintainOffset
18235      * @type boolean
18236      */
18237     maintainOffset: false,
18238
18239     /**
18240      * Array of pixel locations the element will snap to if we specified a
18241      * horizontal graduation/interval.  This array is generated automatically
18242      * when you define a tick interval.
18243      * @property xTicks
18244      * @type int[]
18245      */
18246     xTicks: null,
18247
18248     /**
18249      * Array of pixel locations the element will snap to if we specified a
18250      * vertical graduation/interval.  This array is generated automatically
18251      * when you define a tick interval.
18252      * @property yTicks
18253      * @type int[]
18254      */
18255     yTicks: null,
18256
18257     /**
18258      * By default the drag and drop instance will only respond to the primary
18259      * button click (left button for a right-handed mouse).  Set to true to
18260      * allow drag and drop to start with any mouse click that is propogated
18261      * by the browser
18262      * @property primaryButtonOnly
18263      * @type boolean
18264      */
18265     primaryButtonOnly: true,
18266
18267     /**
18268      * The availabe property is false until the linked dom element is accessible.
18269      * @property available
18270      * @type boolean
18271      */
18272     available: false,
18273
18274     /**
18275      * By default, drags can only be initiated if the mousedown occurs in the
18276      * region the linked element is.  This is done in part to work around a
18277      * bug in some browsers that mis-report the mousedown if the previous
18278      * mouseup happened outside of the window.  This property is set to true
18279      * if outer handles are defined.
18280      *
18281      * @property hasOuterHandles
18282      * @type boolean
18283      * @default false
18284      */
18285     hasOuterHandles: false,
18286
18287     /**
18288      * Code that executes immediately before the startDrag event
18289      * @method b4StartDrag
18290      * @private
18291      */
18292     b4StartDrag: function(x, y) { },
18293
18294     /**
18295      * Abstract method called after a drag/drop object is clicked
18296      * and the drag or mousedown time thresholds have beeen met.
18297      * @method startDrag
18298      * @param {int} X click location
18299      * @param {int} Y click location
18300      */
18301     startDrag: function(x, y) { /* override this */ },
18302
18303     /**
18304      * Code that executes immediately before the onDrag event
18305      * @method b4Drag
18306      * @private
18307      */
18308     b4Drag: function(e) { },
18309
18310     /**
18311      * Abstract method called during the onMouseMove event while dragging an
18312      * object.
18313      * @method onDrag
18314      * @param {Event} e the mousemove event
18315      */
18316     onDrag: function(e) { /* override this */ },
18317
18318     /**
18319      * Abstract method called when this element fist begins hovering over
18320      * another DragDrop obj
18321      * @method onDragEnter
18322      * @param {Event} e the mousemove event
18323      * @param {String|DragDrop[]} id In POINT mode, the element
18324      * id this is hovering over.  In INTERSECT mode, an array of one or more
18325      * dragdrop items being hovered over.
18326      */
18327     onDragEnter: function(e, id) { /* override this */ },
18328
18329     /**
18330      * Code that executes immediately before the onDragOver event
18331      * @method b4DragOver
18332      * @private
18333      */
18334     b4DragOver: function(e) { },
18335
18336     /**
18337      * Abstract method called when this element is hovering over another
18338      * DragDrop obj
18339      * @method onDragOver
18340      * @param {Event} e the mousemove event
18341      * @param {String|DragDrop[]} id In POINT mode, the element
18342      * id this is hovering over.  In INTERSECT mode, an array of dd items
18343      * being hovered over.
18344      */
18345     onDragOver: function(e, id) { /* override this */ },
18346
18347     /**
18348      * Code that executes immediately before the onDragOut event
18349      * @method b4DragOut
18350      * @private
18351      */
18352     b4DragOut: function(e) { },
18353
18354     /**
18355      * Abstract method called when we are no longer hovering over an element
18356      * @method onDragOut
18357      * @param {Event} e the mousemove event
18358      * @param {String|DragDrop[]} id In POINT mode, the element
18359      * id this was hovering over.  In INTERSECT mode, an array of dd items
18360      * that the mouse is no longer over.
18361      */
18362     onDragOut: function(e, id) { /* override this */ },
18363
18364     /**
18365      * Code that executes immediately before the onDragDrop event
18366      * @method b4DragDrop
18367      * @private
18368      */
18369     b4DragDrop: function(e) { },
18370
18371     /**
18372      * Abstract method called when this item is dropped on another DragDrop
18373      * obj
18374      * @method onDragDrop
18375      * @param {Event} e the mouseup event
18376      * @param {String|DragDrop[]} id In POINT mode, the element
18377      * id this was dropped on.  In INTERSECT mode, an array of dd items this
18378      * was dropped on.
18379      */
18380     onDragDrop: function(e, id) { /* override this */ },
18381
18382     /**
18383      * Abstract method called when this item is dropped on an area with no
18384      * drop target
18385      * @method onInvalidDrop
18386      * @param {Event} e the mouseup event
18387      */
18388     onInvalidDrop: function(e) { /* override this */ },
18389
18390     /**
18391      * Code that executes immediately before the endDrag event
18392      * @method b4EndDrag
18393      * @private
18394      */
18395     b4EndDrag: function(e) { },
18396
18397     /**
18398      * Fired when we are done dragging the object
18399      * @method endDrag
18400      * @param {Event} e the mouseup event
18401      */
18402     endDrag: function(e) { /* override this */ },
18403
18404     /**
18405      * Code executed immediately before the onMouseDown event
18406      * @method b4MouseDown
18407      * @param {Event} e the mousedown event
18408      * @private
18409      */
18410     b4MouseDown: function(e) {  },
18411
18412     /**
18413      * Event handler that fires when a drag/drop obj gets a mousedown
18414      * @method onMouseDown
18415      * @param {Event} e the mousedown event
18416      */
18417     onMouseDown: function(e) { /* override this */ },
18418
18419     /**
18420      * Event handler that fires when a drag/drop obj gets a mouseup
18421      * @method onMouseUp
18422      * @param {Event} e the mouseup event
18423      */
18424     onMouseUp: function(e) { /* override this */ },
18425
18426     /**
18427      * Override the onAvailable method to do what is needed after the initial
18428      * position was determined.
18429      * @method onAvailable
18430      */
18431     onAvailable: function () {
18432     },
18433
18434     /*
18435      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
18436      * @type Object
18437      */
18438     defaultPadding : {left:0, right:0, top:0, bottom:0},
18439
18440     /*
18441      * Initializes the drag drop object's constraints to restrict movement to a certain element.
18442  *
18443  * Usage:
18444  <pre><code>
18445  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
18446                 { dragElId: "existingProxyDiv" });
18447  dd.startDrag = function(){
18448      this.constrainTo("parent-id");
18449  };
18450  </code></pre>
18451  * Or you can initalize it using the {@link Roo.Element} object:
18452  <pre><code>
18453  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
18454      startDrag : function(){
18455          this.constrainTo("parent-id");
18456      }
18457  });
18458  </code></pre>
18459      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
18460      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
18461      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
18462      * an object containing the sides to pad. For example: {right:10, bottom:10}
18463      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
18464      */
18465     constrainTo : function(constrainTo, pad, inContent){
18466         if(typeof pad == "number"){
18467             pad = {left: pad, right:pad, top:pad, bottom:pad};
18468         }
18469         pad = pad || this.defaultPadding;
18470         var b = Roo.get(this.getEl()).getBox();
18471         var ce = Roo.get(constrainTo);
18472         var s = ce.getScroll();
18473         var c, cd = ce.dom;
18474         if(cd == document.body){
18475             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
18476         }else{
18477             xy = ce.getXY();
18478             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
18479         }
18480
18481
18482         var topSpace = b.y - c.y;
18483         var leftSpace = b.x - c.x;
18484
18485         this.resetConstraints();
18486         this.setXConstraint(leftSpace - (pad.left||0), // left
18487                 c.width - leftSpace - b.width - (pad.right||0) //right
18488         );
18489         this.setYConstraint(topSpace - (pad.top||0), //top
18490                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
18491         );
18492     },
18493
18494     /**
18495      * Returns a reference to the linked element
18496      * @method getEl
18497      * @return {HTMLElement} the html element
18498      */
18499     getEl: function() {
18500         if (!this._domRef) {
18501             this._domRef = Roo.getDom(this.id);
18502         }
18503
18504         return this._domRef;
18505     },
18506
18507     /**
18508      * Returns a reference to the actual element to drag.  By default this is
18509      * the same as the html element, but it can be assigned to another
18510      * element. An example of this can be found in Roo.dd.DDProxy
18511      * @method getDragEl
18512      * @return {HTMLElement} the html element
18513      */
18514     getDragEl: function() {
18515         return Roo.getDom(this.dragElId);
18516     },
18517
18518     /**
18519      * Sets up the DragDrop object.  Must be called in the constructor of any
18520      * Roo.dd.DragDrop subclass
18521      * @method init
18522      * @param id the id of the linked element
18523      * @param {String} sGroup the group of related items
18524      * @param {object} config configuration attributes
18525      */
18526     init: function(id, sGroup, config) {
18527         this.initTarget(id, sGroup, config);
18528         if (!Roo.isTouch) {
18529             Event.on(this.id, "mousedown", this.handleMouseDown, this);
18530         }
18531         Event.on(this.id, "touchstart", this.handleMouseDown, this);
18532         // Event.on(this.id, "selectstart", Event.preventDefault);
18533     },
18534
18535     /**
18536      * Initializes Targeting functionality only... the object does not
18537      * get a mousedown handler.
18538      * @method initTarget
18539      * @param id the id of the linked element
18540      * @param {String} sGroup the group of related items
18541      * @param {object} config configuration attributes
18542      */
18543     initTarget: function(id, sGroup, config) {
18544
18545         // configuration attributes
18546         this.config = config || {};
18547
18548         // create a local reference to the drag and drop manager
18549         this.DDM = Roo.dd.DDM;
18550         // initialize the groups array
18551         this.groups = {};
18552
18553         // assume that we have an element reference instead of an id if the
18554         // parameter is not a string
18555         if (typeof id !== "string") {
18556             id = Roo.id(id);
18557         }
18558
18559         // set the id
18560         this.id = id;
18561
18562         // add to an interaction group
18563         this.addToGroup((sGroup) ? sGroup : "default");
18564
18565         // We don't want to register this as the handle with the manager
18566         // so we just set the id rather than calling the setter.
18567         this.handleElId = id;
18568
18569         // the linked element is the element that gets dragged by default
18570         this.setDragElId(id);
18571
18572         // by default, clicked anchors will not start drag operations.
18573         this.invalidHandleTypes = { A: "A" };
18574         this.invalidHandleIds = {};
18575         this.invalidHandleClasses = [];
18576
18577         this.applyConfig();
18578
18579         this.handleOnAvailable();
18580     },
18581
18582     /**
18583      * Applies the configuration parameters that were passed into the constructor.
18584      * This is supposed to happen at each level through the inheritance chain.  So
18585      * a DDProxy implentation will execute apply config on DDProxy, DD, and
18586      * DragDrop in order to get all of the parameters that are available in
18587      * each object.
18588      * @method applyConfig
18589      */
18590     applyConfig: function() {
18591
18592         // configurable properties:
18593         //    padding, isTarget, maintainOffset, primaryButtonOnly
18594         this.padding           = this.config.padding || [0, 0, 0, 0];
18595         this.isTarget          = (this.config.isTarget !== false);
18596         this.maintainOffset    = (this.config.maintainOffset);
18597         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
18598
18599     },
18600
18601     /**
18602      * Executed when the linked element is available
18603      * @method handleOnAvailable
18604      * @private
18605      */
18606     handleOnAvailable: function() {
18607         this.available = true;
18608         this.resetConstraints();
18609         this.onAvailable();
18610     },
18611
18612      /**
18613      * Configures the padding for the target zone in px.  Effectively expands
18614      * (or reduces) the virtual object size for targeting calculations.
18615      * Supports css-style shorthand; if only one parameter is passed, all sides
18616      * will have that padding, and if only two are passed, the top and bottom
18617      * will have the first param, the left and right the second.
18618      * @method setPadding
18619      * @param {int} iTop    Top pad
18620      * @param {int} iRight  Right pad
18621      * @param {int} iBot    Bot pad
18622      * @param {int} iLeft   Left pad
18623      */
18624     setPadding: function(iTop, iRight, iBot, iLeft) {
18625         // this.padding = [iLeft, iRight, iTop, iBot];
18626         if (!iRight && 0 !== iRight) {
18627             this.padding = [iTop, iTop, iTop, iTop];
18628         } else if (!iBot && 0 !== iBot) {
18629             this.padding = [iTop, iRight, iTop, iRight];
18630         } else {
18631             this.padding = [iTop, iRight, iBot, iLeft];
18632         }
18633     },
18634
18635     /**
18636      * Stores the initial placement of the linked element.
18637      * @method setInitialPosition
18638      * @param {int} diffX   the X offset, default 0
18639      * @param {int} diffY   the Y offset, default 0
18640      */
18641     setInitPosition: function(diffX, diffY) {
18642         var el = this.getEl();
18643
18644         if (!this.DDM.verifyEl(el)) {
18645             return;
18646         }
18647
18648         var dx = diffX || 0;
18649         var dy = diffY || 0;
18650
18651         var p = Dom.getXY( el );
18652
18653         this.initPageX = p[0] - dx;
18654         this.initPageY = p[1] - dy;
18655
18656         this.lastPageX = p[0];
18657         this.lastPageY = p[1];
18658
18659
18660         this.setStartPosition(p);
18661     },
18662
18663     /**
18664      * Sets the start position of the element.  This is set when the obj
18665      * is initialized, the reset when a drag is started.
18666      * @method setStartPosition
18667      * @param pos current position (from previous lookup)
18668      * @private
18669      */
18670     setStartPosition: function(pos) {
18671         var p = pos || Dom.getXY( this.getEl() );
18672         this.deltaSetXY = null;
18673
18674         this.startPageX = p[0];
18675         this.startPageY = p[1];
18676     },
18677
18678     /**
18679      * Add this instance to a group of related drag/drop objects.  All
18680      * instances belong to at least one group, and can belong to as many
18681      * groups as needed.
18682      * @method addToGroup
18683      * @param sGroup {string} the name of the group
18684      */
18685     addToGroup: function(sGroup) {
18686         this.groups[sGroup] = true;
18687         this.DDM.regDragDrop(this, sGroup);
18688     },
18689
18690     /**
18691      * Remove's this instance from the supplied interaction group
18692      * @method removeFromGroup
18693      * @param {string}  sGroup  The group to drop
18694      */
18695     removeFromGroup: function(sGroup) {
18696         if (this.groups[sGroup]) {
18697             delete this.groups[sGroup];
18698         }
18699
18700         this.DDM.removeDDFromGroup(this, sGroup);
18701     },
18702
18703     /**
18704      * Allows you to specify that an element other than the linked element
18705      * will be moved with the cursor during a drag
18706      * @method setDragElId
18707      * @param id {string} the id of the element that will be used to initiate the drag
18708      */
18709     setDragElId: function(id) {
18710         this.dragElId = id;
18711     },
18712
18713     /**
18714      * Allows you to specify a child of the linked element that should be
18715      * used to initiate the drag operation.  An example of this would be if
18716      * you have a content div with text and links.  Clicking anywhere in the
18717      * content area would normally start the drag operation.  Use this method
18718      * to specify that an element inside of the content div is the element
18719      * that starts the drag operation.
18720      * @method setHandleElId
18721      * @param id {string} the id of the element that will be used to
18722      * initiate the drag.
18723      */
18724     setHandleElId: function(id) {
18725         if (typeof id !== "string") {
18726             id = Roo.id(id);
18727         }
18728         this.handleElId = id;
18729         this.DDM.regHandle(this.id, id);
18730     },
18731
18732     /**
18733      * Allows you to set an element outside of the linked element as a drag
18734      * handle
18735      * @method setOuterHandleElId
18736      * @param id the id of the element that will be used to initiate the drag
18737      */
18738     setOuterHandleElId: function(id) {
18739         if (typeof id !== "string") {
18740             id = Roo.id(id);
18741         }
18742         Event.on(id, "mousedown",
18743                 this.handleMouseDown, this);
18744         this.setHandleElId(id);
18745
18746         this.hasOuterHandles = true;
18747     },
18748
18749     /**
18750      * Remove all drag and drop hooks for this element
18751      * @method unreg
18752      */
18753     unreg: function() {
18754         Event.un(this.id, "mousedown",
18755                 this.handleMouseDown);
18756         Event.un(this.id, "touchstart",
18757                 this.handleMouseDown);
18758         this._domRef = null;
18759         this.DDM._remove(this);
18760     },
18761
18762     destroy : function(){
18763         this.unreg();
18764     },
18765
18766     /**
18767      * Returns true if this instance is locked, or the drag drop mgr is locked
18768      * (meaning that all drag/drop is disabled on the page.)
18769      * @method isLocked
18770      * @return {boolean} true if this obj or all drag/drop is locked, else
18771      * false
18772      */
18773     isLocked: function() {
18774         return (this.DDM.isLocked() || this.locked);
18775     },
18776
18777     /**
18778      * Fired when this object is clicked
18779      * @method handleMouseDown
18780      * @param {Event} e
18781      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
18782      * @private
18783      */
18784     handleMouseDown: function(e, oDD){
18785      
18786         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
18787             //Roo.log('not touch/ button !=0');
18788             return;
18789         }
18790         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
18791             return; // double touch..
18792         }
18793         
18794
18795         if (this.isLocked()) {
18796             //Roo.log('locked');
18797             return;
18798         }
18799
18800         this.DDM.refreshCache(this.groups);
18801 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
18802         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
18803         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
18804             //Roo.log('no outer handes or not over target');
18805                 // do nothing.
18806         } else {
18807 //            Roo.log('check validator');
18808             if (this.clickValidator(e)) {
18809 //                Roo.log('validate success');
18810                 // set the initial element position
18811                 this.setStartPosition();
18812
18813
18814                 this.b4MouseDown(e);
18815                 this.onMouseDown(e);
18816
18817                 this.DDM.handleMouseDown(e, this);
18818
18819                 this.DDM.stopEvent(e);
18820             } else {
18821
18822
18823             }
18824         }
18825     },
18826
18827     clickValidator: function(e) {
18828         var target = e.getTarget();
18829         return ( this.isValidHandleChild(target) &&
18830                     (this.id == this.handleElId ||
18831                         this.DDM.handleWasClicked(target, this.id)) );
18832     },
18833
18834     /**
18835      * Allows you to specify a tag name that should not start a drag operation
18836      * when clicked.  This is designed to facilitate embedding links within a
18837      * drag handle that do something other than start the drag.
18838      * @method addInvalidHandleType
18839      * @param {string} tagName the type of element to exclude
18840      */
18841     addInvalidHandleType: function(tagName) {
18842         var type = tagName.toUpperCase();
18843         this.invalidHandleTypes[type] = type;
18844     },
18845
18846     /**
18847      * Lets you to specify an element id for a child of a drag handle
18848      * that should not initiate a drag
18849      * @method addInvalidHandleId
18850      * @param {string} id the element id of the element you wish to ignore
18851      */
18852     addInvalidHandleId: function(id) {
18853         if (typeof id !== "string") {
18854             id = Roo.id(id);
18855         }
18856         this.invalidHandleIds[id] = id;
18857     },
18858
18859     /**
18860      * Lets you specify a css class of elements that will not initiate a drag
18861      * @method addInvalidHandleClass
18862      * @param {string} cssClass the class of the elements you wish to ignore
18863      */
18864     addInvalidHandleClass: function(cssClass) {
18865         this.invalidHandleClasses.push(cssClass);
18866     },
18867
18868     /**
18869      * Unsets an excluded tag name set by addInvalidHandleType
18870      * @method removeInvalidHandleType
18871      * @param {string} tagName the type of element to unexclude
18872      */
18873     removeInvalidHandleType: function(tagName) {
18874         var type = tagName.toUpperCase();
18875         // this.invalidHandleTypes[type] = null;
18876         delete this.invalidHandleTypes[type];
18877     },
18878
18879     /**
18880      * Unsets an invalid handle id
18881      * @method removeInvalidHandleId
18882      * @param {string} id the id of the element to re-enable
18883      */
18884     removeInvalidHandleId: function(id) {
18885         if (typeof id !== "string") {
18886             id = Roo.id(id);
18887         }
18888         delete this.invalidHandleIds[id];
18889     },
18890
18891     /**
18892      * Unsets an invalid css class
18893      * @method removeInvalidHandleClass
18894      * @param {string} cssClass the class of the element(s) you wish to
18895      * re-enable
18896      */
18897     removeInvalidHandleClass: function(cssClass) {
18898         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
18899             if (this.invalidHandleClasses[i] == cssClass) {
18900                 delete this.invalidHandleClasses[i];
18901             }
18902         }
18903     },
18904
18905     /**
18906      * Checks the tag exclusion list to see if this click should be ignored
18907      * @method isValidHandleChild
18908      * @param {HTMLElement} node the HTMLElement to evaluate
18909      * @return {boolean} true if this is a valid tag type, false if not
18910      */
18911     isValidHandleChild: function(node) {
18912
18913         var valid = true;
18914         // var n = (node.nodeName == "#text") ? node.parentNode : node;
18915         var nodeName;
18916         try {
18917             nodeName = node.nodeName.toUpperCase();
18918         } catch(e) {
18919             nodeName = node.nodeName;
18920         }
18921         valid = valid && !this.invalidHandleTypes[nodeName];
18922         valid = valid && !this.invalidHandleIds[node.id];
18923
18924         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
18925             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
18926         }
18927
18928
18929         return valid;
18930
18931     },
18932
18933     /**
18934      * Create the array of horizontal tick marks if an interval was specified
18935      * in setXConstraint().
18936      * @method setXTicks
18937      * @private
18938      */
18939     setXTicks: function(iStartX, iTickSize) {
18940         this.xTicks = [];
18941         this.xTickSize = iTickSize;
18942
18943         var tickMap = {};
18944
18945         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
18946             if (!tickMap[i]) {
18947                 this.xTicks[this.xTicks.length] = i;
18948                 tickMap[i] = true;
18949             }
18950         }
18951
18952         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
18953             if (!tickMap[i]) {
18954                 this.xTicks[this.xTicks.length] = i;
18955                 tickMap[i] = true;
18956             }
18957         }
18958
18959         this.xTicks.sort(this.DDM.numericSort) ;
18960     },
18961
18962     /**
18963      * Create the array of vertical tick marks if an interval was specified in
18964      * setYConstraint().
18965      * @method setYTicks
18966      * @private
18967      */
18968     setYTicks: function(iStartY, iTickSize) {
18969         this.yTicks = [];
18970         this.yTickSize = iTickSize;
18971
18972         var tickMap = {};
18973
18974         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
18975             if (!tickMap[i]) {
18976                 this.yTicks[this.yTicks.length] = i;
18977                 tickMap[i] = true;
18978             }
18979         }
18980
18981         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
18982             if (!tickMap[i]) {
18983                 this.yTicks[this.yTicks.length] = i;
18984                 tickMap[i] = true;
18985             }
18986         }
18987
18988         this.yTicks.sort(this.DDM.numericSort) ;
18989     },
18990
18991     /**
18992      * By default, the element can be dragged any place on the screen.  Use
18993      * this method to limit the horizontal travel of the element.  Pass in
18994      * 0,0 for the parameters if you want to lock the drag to the y axis.
18995      * @method setXConstraint
18996      * @param {int} iLeft the number of pixels the element can move to the left
18997      * @param {int} iRight the number of pixels the element can move to the
18998      * right
18999      * @param {int} iTickSize optional parameter for specifying that the
19000      * element
19001      * should move iTickSize pixels at a time.
19002      */
19003     setXConstraint: function(iLeft, iRight, iTickSize) {
19004         this.leftConstraint = iLeft;
19005         this.rightConstraint = iRight;
19006
19007         this.minX = this.initPageX - iLeft;
19008         this.maxX = this.initPageX + iRight;
19009         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19010
19011         this.constrainX = true;
19012     },
19013
19014     /**
19015      * Clears any constraints applied to this instance.  Also clears ticks
19016      * since they can't exist independent of a constraint at this time.
19017      * @method clearConstraints
19018      */
19019     clearConstraints: function() {
19020         this.constrainX = false;
19021         this.constrainY = false;
19022         this.clearTicks();
19023     },
19024
19025     /**
19026      * Clears any tick interval defined for this instance
19027      * @method clearTicks
19028      */
19029     clearTicks: function() {
19030         this.xTicks = null;
19031         this.yTicks = null;
19032         this.xTickSize = 0;
19033         this.yTickSize = 0;
19034     },
19035
19036     /**
19037      * By default, the element can be dragged any place on the screen.  Set
19038      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19039      * parameters if you want to lock the drag to the x axis.
19040      * @method setYConstraint
19041      * @param {int} iUp the number of pixels the element can move up
19042      * @param {int} iDown the number of pixels the element can move down
19043      * @param {int} iTickSize optional parameter for specifying that the
19044      * element should move iTickSize pixels at a time.
19045      */
19046     setYConstraint: function(iUp, iDown, iTickSize) {
19047         this.topConstraint = iUp;
19048         this.bottomConstraint = iDown;
19049
19050         this.minY = this.initPageY - iUp;
19051         this.maxY = this.initPageY + iDown;
19052         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19053
19054         this.constrainY = true;
19055
19056     },
19057
19058     /**
19059      * resetConstraints must be called if you manually reposition a dd element.
19060      * @method resetConstraints
19061      * @param {boolean} maintainOffset
19062      */
19063     resetConstraints: function() {
19064
19065
19066         // Maintain offsets if necessary
19067         if (this.initPageX || this.initPageX === 0) {
19068             // figure out how much this thing has moved
19069             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19070             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19071
19072             this.setInitPosition(dx, dy);
19073
19074         // This is the first time we have detected the element's position
19075         } else {
19076             this.setInitPosition();
19077         }
19078
19079         if (this.constrainX) {
19080             this.setXConstraint( this.leftConstraint,
19081                                  this.rightConstraint,
19082                                  this.xTickSize        );
19083         }
19084
19085         if (this.constrainY) {
19086             this.setYConstraint( this.topConstraint,
19087                                  this.bottomConstraint,
19088                                  this.yTickSize         );
19089         }
19090     },
19091
19092     /**
19093      * Normally the drag element is moved pixel by pixel, but we can specify
19094      * that it move a number of pixels at a time.  This method resolves the
19095      * location when we have it set up like this.
19096      * @method getTick
19097      * @param {int} val where we want to place the object
19098      * @param {int[]} tickArray sorted array of valid points
19099      * @return {int} the closest tick
19100      * @private
19101      */
19102     getTick: function(val, tickArray) {
19103
19104         if (!tickArray) {
19105             // If tick interval is not defined, it is effectively 1 pixel,
19106             // so we return the value passed to us.
19107             return val;
19108         } else if (tickArray[0] >= val) {
19109             // The value is lower than the first tick, so we return the first
19110             // tick.
19111             return tickArray[0];
19112         } else {
19113             for (var i=0, len=tickArray.length; i<len; ++i) {
19114                 var next = i + 1;
19115                 if (tickArray[next] && tickArray[next] >= val) {
19116                     var diff1 = val - tickArray[i];
19117                     var diff2 = tickArray[next] - val;
19118                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19119                 }
19120             }
19121
19122             // The value is larger than the last tick, so we return the last
19123             // tick.
19124             return tickArray[tickArray.length - 1];
19125         }
19126     },
19127
19128     /**
19129      * toString method
19130      * @method toString
19131      * @return {string} string representation of the dd obj
19132      */
19133     toString: function() {
19134         return ("DragDrop " + this.id);
19135     }
19136
19137 });
19138
19139 })();
19140 /*
19141  * Based on:
19142  * Ext JS Library 1.1.1
19143  * Copyright(c) 2006-2007, Ext JS, LLC.
19144  *
19145  * Originally Released Under LGPL - original licence link has changed is not relivant.
19146  *
19147  * Fork - LGPL
19148  * <script type="text/javascript">
19149  */
19150
19151
19152 /**
19153  * The drag and drop utility provides a framework for building drag and drop
19154  * applications.  In addition to enabling drag and drop for specific elements,
19155  * the drag and drop elements are tracked by the manager class, and the
19156  * interactions between the various elements are tracked during the drag and
19157  * the implementing code is notified about these important moments.
19158  */
19159
19160 // Only load the library once.  Rewriting the manager class would orphan
19161 // existing drag and drop instances.
19162 if (!Roo.dd.DragDropMgr) {
19163
19164 /**
19165  * @class Roo.dd.DragDropMgr
19166  * DragDropMgr is a singleton that tracks the element interaction for
19167  * all DragDrop items in the window.  Generally, you will not call
19168  * this class directly, but it does have helper methods that could
19169  * be useful in your DragDrop implementations.
19170  * @singleton
19171  */
19172 Roo.dd.DragDropMgr = function() {
19173
19174     var Event = Roo.EventManager;
19175
19176     return {
19177
19178         /**
19179          * Two dimensional Array of registered DragDrop objects.  The first
19180          * dimension is the DragDrop item group, the second the DragDrop
19181          * object.
19182          * @property ids
19183          * @type {string: string}
19184          * @private
19185          * @static
19186          */
19187         ids: {},
19188
19189         /**
19190          * Array of element ids defined as drag handles.  Used to determine
19191          * if the element that generated the mousedown event is actually the
19192          * handle and not the html element itself.
19193          * @property handleIds
19194          * @type {string: string}
19195          * @private
19196          * @static
19197          */
19198         handleIds: {},
19199
19200         /**
19201          * the DragDrop object that is currently being dragged
19202          * @property dragCurrent
19203          * @type DragDrop
19204          * @private
19205          * @static
19206          **/
19207         dragCurrent: null,
19208
19209         /**
19210          * the DragDrop object(s) that are being hovered over
19211          * @property dragOvers
19212          * @type Array
19213          * @private
19214          * @static
19215          */
19216         dragOvers: {},
19217
19218         /**
19219          * the X distance between the cursor and the object being dragged
19220          * @property deltaX
19221          * @type int
19222          * @private
19223          * @static
19224          */
19225         deltaX: 0,
19226
19227         /**
19228          * the Y distance between the cursor and the object being dragged
19229          * @property deltaY
19230          * @type int
19231          * @private
19232          * @static
19233          */
19234         deltaY: 0,
19235
19236         /**
19237          * Flag to determine if we should prevent the default behavior of the
19238          * events we define. By default this is true, but this can be set to
19239          * false if you need the default behavior (not recommended)
19240          * @property preventDefault
19241          * @type boolean
19242          * @static
19243          */
19244         preventDefault: true,
19245
19246         /**
19247          * Flag to determine if we should stop the propagation of the events
19248          * we generate. This is true by default but you may want to set it to
19249          * false if the html element contains other features that require the
19250          * mouse click.
19251          * @property stopPropagation
19252          * @type boolean
19253          * @static
19254          */
19255         stopPropagation: true,
19256
19257         /**
19258          * Internal flag that is set to true when drag and drop has been
19259          * intialized
19260          * @property initialized
19261          * @private
19262          * @static
19263          */
19264         initalized: false,
19265
19266         /**
19267          * All drag and drop can be disabled.
19268          * @property locked
19269          * @private
19270          * @static
19271          */
19272         locked: false,
19273
19274         /**
19275          * Called the first time an element is registered.
19276          * @method init
19277          * @private
19278          * @static
19279          */
19280         init: function() {
19281             this.initialized = true;
19282         },
19283
19284         /**
19285          * In point mode, drag and drop interaction is defined by the
19286          * location of the cursor during the drag/drop
19287          * @property POINT
19288          * @type int
19289          * @static
19290          */
19291         POINT: 0,
19292
19293         /**
19294          * In intersect mode, drag and drop interactio nis defined by the
19295          * overlap of two or more drag and drop objects.
19296          * @property INTERSECT
19297          * @type int
19298          * @static
19299          */
19300         INTERSECT: 1,
19301
19302         /**
19303          * The current drag and drop mode.  Default: POINT
19304          * @property mode
19305          * @type int
19306          * @static
19307          */
19308         mode: 0,
19309
19310         /**
19311          * Runs method on all drag and drop objects
19312          * @method _execOnAll
19313          * @private
19314          * @static
19315          */
19316         _execOnAll: function(sMethod, args) {
19317             for (var i in this.ids) {
19318                 for (var j in this.ids[i]) {
19319                     var oDD = this.ids[i][j];
19320                     if (! this.isTypeOfDD(oDD)) {
19321                         continue;
19322                     }
19323                     oDD[sMethod].apply(oDD, args);
19324                 }
19325             }
19326         },
19327
19328         /**
19329          * Drag and drop initialization.  Sets up the global event handlers
19330          * @method _onLoad
19331          * @private
19332          * @static
19333          */
19334         _onLoad: function() {
19335
19336             this.init();
19337
19338             if (!Roo.isTouch) {
19339                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19340                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19341             }
19342             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19343             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19344             
19345             Event.on(window,   "unload",    this._onUnload, this, true);
19346             Event.on(window,   "resize",    this._onResize, this, true);
19347             // Event.on(window,   "mouseout",    this._test);
19348
19349         },
19350
19351         /**
19352          * Reset constraints on all drag and drop objs
19353          * @method _onResize
19354          * @private
19355          * @static
19356          */
19357         _onResize: function(e) {
19358             this._execOnAll("resetConstraints", []);
19359         },
19360
19361         /**
19362          * Lock all drag and drop functionality
19363          * @method lock
19364          * @static
19365          */
19366         lock: function() { this.locked = true; },
19367
19368         /**
19369          * Unlock all drag and drop functionality
19370          * @method unlock
19371          * @static
19372          */
19373         unlock: function() { this.locked = false; },
19374
19375         /**
19376          * Is drag and drop locked?
19377          * @method isLocked
19378          * @return {boolean} True if drag and drop is locked, false otherwise.
19379          * @static
19380          */
19381         isLocked: function() { return this.locked; },
19382
19383         /**
19384          * Location cache that is set for all drag drop objects when a drag is
19385          * initiated, cleared when the drag is finished.
19386          * @property locationCache
19387          * @private
19388          * @static
19389          */
19390         locationCache: {},
19391
19392         /**
19393          * Set useCache to false if you want to force object the lookup of each
19394          * drag and drop linked element constantly during a drag.
19395          * @property useCache
19396          * @type boolean
19397          * @static
19398          */
19399         useCache: true,
19400
19401         /**
19402          * The number of pixels that the mouse needs to move after the
19403          * mousedown before the drag is initiated.  Default=3;
19404          * @property clickPixelThresh
19405          * @type int
19406          * @static
19407          */
19408         clickPixelThresh: 3,
19409
19410         /**
19411          * The number of milliseconds after the mousedown event to initiate the
19412          * drag if we don't get a mouseup event. Default=1000
19413          * @property clickTimeThresh
19414          * @type int
19415          * @static
19416          */
19417         clickTimeThresh: 350,
19418
19419         /**
19420          * Flag that indicates that either the drag pixel threshold or the
19421          * mousdown time threshold has been met
19422          * @property dragThreshMet
19423          * @type boolean
19424          * @private
19425          * @static
19426          */
19427         dragThreshMet: false,
19428
19429         /**
19430          * Timeout used for the click time threshold
19431          * @property clickTimeout
19432          * @type Object
19433          * @private
19434          * @static
19435          */
19436         clickTimeout: null,
19437
19438         /**
19439          * The X position of the mousedown event stored for later use when a
19440          * drag threshold is met.
19441          * @property startX
19442          * @type int
19443          * @private
19444          * @static
19445          */
19446         startX: 0,
19447
19448         /**
19449          * The Y position of the mousedown event stored for later use when a
19450          * drag threshold is met.
19451          * @property startY
19452          * @type int
19453          * @private
19454          * @static
19455          */
19456         startY: 0,
19457
19458         /**
19459          * Each DragDrop instance must be registered with the DragDropMgr.
19460          * This is executed in DragDrop.init()
19461          * @method regDragDrop
19462          * @param {DragDrop} oDD the DragDrop object to register
19463          * @param {String} sGroup the name of the group this element belongs to
19464          * @static
19465          */
19466         regDragDrop: function(oDD, sGroup) {
19467             if (!this.initialized) { this.init(); }
19468
19469             if (!this.ids[sGroup]) {
19470                 this.ids[sGroup] = {};
19471             }
19472             this.ids[sGroup][oDD.id] = oDD;
19473         },
19474
19475         /**
19476          * Removes the supplied dd instance from the supplied group. Executed
19477          * by DragDrop.removeFromGroup, so don't call this function directly.
19478          * @method removeDDFromGroup
19479          * @private
19480          * @static
19481          */
19482         removeDDFromGroup: function(oDD, sGroup) {
19483             if (!this.ids[sGroup]) {
19484                 this.ids[sGroup] = {};
19485             }
19486
19487             var obj = this.ids[sGroup];
19488             if (obj && obj[oDD.id]) {
19489                 delete obj[oDD.id];
19490             }
19491         },
19492
19493         /**
19494          * Unregisters a drag and drop item.  This is executed in
19495          * DragDrop.unreg, use that method instead of calling this directly.
19496          * @method _remove
19497          * @private
19498          * @static
19499          */
19500         _remove: function(oDD) {
19501             for (var g in oDD.groups) {
19502                 if (g && this.ids[g][oDD.id]) {
19503                     delete this.ids[g][oDD.id];
19504                 }
19505             }
19506             delete this.handleIds[oDD.id];
19507         },
19508
19509         /**
19510          * Each DragDrop handle element must be registered.  This is done
19511          * automatically when executing DragDrop.setHandleElId()
19512          * @method regHandle
19513          * @param {String} sDDId the DragDrop id this element is a handle for
19514          * @param {String} sHandleId the id of the element that is the drag
19515          * handle
19516          * @static
19517          */
19518         regHandle: function(sDDId, sHandleId) {
19519             if (!this.handleIds[sDDId]) {
19520                 this.handleIds[sDDId] = {};
19521             }
19522             this.handleIds[sDDId][sHandleId] = sHandleId;
19523         },
19524
19525         /**
19526          * Utility function to determine if a given element has been
19527          * registered as a drag drop item.
19528          * @method isDragDrop
19529          * @param {String} id the element id to check
19530          * @return {boolean} true if this element is a DragDrop item,
19531          * false otherwise
19532          * @static
19533          */
19534         isDragDrop: function(id) {
19535             return ( this.getDDById(id) ) ? true : false;
19536         },
19537
19538         /**
19539          * Returns the drag and drop instances that are in all groups the
19540          * passed in instance belongs to.
19541          * @method getRelated
19542          * @param {DragDrop} p_oDD the obj to get related data for
19543          * @param {boolean} bTargetsOnly if true, only return targetable objs
19544          * @return {DragDrop[]} the related instances
19545          * @static
19546          */
19547         getRelated: function(p_oDD, bTargetsOnly) {
19548             var oDDs = [];
19549             for (var i in p_oDD.groups) {
19550                 for (j in this.ids[i]) {
19551                     var dd = this.ids[i][j];
19552                     if (! this.isTypeOfDD(dd)) {
19553                         continue;
19554                     }
19555                     if (!bTargetsOnly || dd.isTarget) {
19556                         oDDs[oDDs.length] = dd;
19557                     }
19558                 }
19559             }
19560
19561             return oDDs;
19562         },
19563
19564         /**
19565          * Returns true if the specified dd target is a legal target for
19566          * the specifice drag obj
19567          * @method isLegalTarget
19568          * @param {DragDrop} the drag obj
19569          * @param {DragDrop} the target
19570          * @return {boolean} true if the target is a legal target for the
19571          * dd obj
19572          * @static
19573          */
19574         isLegalTarget: function (oDD, oTargetDD) {
19575             var targets = this.getRelated(oDD, true);
19576             for (var i=0, len=targets.length;i<len;++i) {
19577                 if (targets[i].id == oTargetDD.id) {
19578                     return true;
19579                 }
19580             }
19581
19582             return false;
19583         },
19584
19585         /**
19586          * My goal is to be able to transparently determine if an object is
19587          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
19588          * returns "object", oDD.constructor.toString() always returns
19589          * "DragDrop" and not the name of the subclass.  So for now it just
19590          * evaluates a well-known variable in DragDrop.
19591          * @method isTypeOfDD
19592          * @param {Object} the object to evaluate
19593          * @return {boolean} true if typeof oDD = DragDrop
19594          * @static
19595          */
19596         isTypeOfDD: function (oDD) {
19597             return (oDD && oDD.__ygDragDrop);
19598         },
19599
19600         /**
19601          * Utility function to determine if a given element has been
19602          * registered as a drag drop handle for the given Drag Drop object.
19603          * @method isHandle
19604          * @param {String} id the element id to check
19605          * @return {boolean} true if this element is a DragDrop handle, false
19606          * otherwise
19607          * @static
19608          */
19609         isHandle: function(sDDId, sHandleId) {
19610             return ( this.handleIds[sDDId] &&
19611                             this.handleIds[sDDId][sHandleId] );
19612         },
19613
19614         /**
19615          * Returns the DragDrop instance for a given id
19616          * @method getDDById
19617          * @param {String} id the id of the DragDrop object
19618          * @return {DragDrop} the drag drop object, null if it is not found
19619          * @static
19620          */
19621         getDDById: function(id) {
19622             for (var i in this.ids) {
19623                 if (this.ids[i][id]) {
19624                     return this.ids[i][id];
19625                 }
19626             }
19627             return null;
19628         },
19629
19630         /**
19631          * Fired after a registered DragDrop object gets the mousedown event.
19632          * Sets up the events required to track the object being dragged
19633          * @method handleMouseDown
19634          * @param {Event} e the event
19635          * @param oDD the DragDrop object being dragged
19636          * @private
19637          * @static
19638          */
19639         handleMouseDown: function(e, oDD) {
19640             if(Roo.QuickTips){
19641                 Roo.QuickTips.disable();
19642             }
19643             this.currentTarget = e.getTarget();
19644
19645             this.dragCurrent = oDD;
19646
19647             var el = oDD.getEl();
19648
19649             // track start position
19650             this.startX = e.getPageX();
19651             this.startY = e.getPageY();
19652
19653             this.deltaX = this.startX - el.offsetLeft;
19654             this.deltaY = this.startY - el.offsetTop;
19655
19656             this.dragThreshMet = false;
19657
19658             this.clickTimeout = setTimeout(
19659                     function() {
19660                         var DDM = Roo.dd.DDM;
19661                         DDM.startDrag(DDM.startX, DDM.startY);
19662                     },
19663                     this.clickTimeThresh );
19664         },
19665
19666         /**
19667          * Fired when either the drag pixel threshol or the mousedown hold
19668          * time threshold has been met.
19669          * @method startDrag
19670          * @param x {int} the X position of the original mousedown
19671          * @param y {int} the Y position of the original mousedown
19672          * @static
19673          */
19674         startDrag: function(x, y) {
19675             clearTimeout(this.clickTimeout);
19676             if (this.dragCurrent) {
19677                 this.dragCurrent.b4StartDrag(x, y);
19678                 this.dragCurrent.startDrag(x, y);
19679             }
19680             this.dragThreshMet = true;
19681         },
19682
19683         /**
19684          * Internal function to handle the mouseup event.  Will be invoked
19685          * from the context of the document.
19686          * @method handleMouseUp
19687          * @param {Event} e the event
19688          * @private
19689          * @static
19690          */
19691         handleMouseUp: function(e) {
19692
19693             if(Roo.QuickTips){
19694                 Roo.QuickTips.enable();
19695             }
19696             if (! this.dragCurrent) {
19697                 return;
19698             }
19699
19700             clearTimeout(this.clickTimeout);
19701
19702             if (this.dragThreshMet) {
19703                 this.fireEvents(e, true);
19704             } else {
19705             }
19706
19707             this.stopDrag(e);
19708
19709             this.stopEvent(e);
19710         },
19711
19712         /**
19713          * Utility to stop event propagation and event default, if these
19714          * features are turned on.
19715          * @method stopEvent
19716          * @param {Event} e the event as returned by this.getEvent()
19717          * @static
19718          */
19719         stopEvent: function(e){
19720             if(this.stopPropagation) {
19721                 e.stopPropagation();
19722             }
19723
19724             if (this.preventDefault) {
19725                 e.preventDefault();
19726             }
19727         },
19728
19729         /**
19730          * Internal function to clean up event handlers after the drag
19731          * operation is complete
19732          * @method stopDrag
19733          * @param {Event} e the event
19734          * @private
19735          * @static
19736          */
19737         stopDrag: function(e) {
19738             // Fire the drag end event for the item that was dragged
19739             if (this.dragCurrent) {
19740                 if (this.dragThreshMet) {
19741                     this.dragCurrent.b4EndDrag(e);
19742                     this.dragCurrent.endDrag(e);
19743                 }
19744
19745                 this.dragCurrent.onMouseUp(e);
19746             }
19747
19748             this.dragCurrent = null;
19749             this.dragOvers = {};
19750         },
19751
19752         /**
19753          * Internal function to handle the mousemove event.  Will be invoked
19754          * from the context of the html element.
19755          *
19756          * @TODO figure out what we can do about mouse events lost when the
19757          * user drags objects beyond the window boundary.  Currently we can
19758          * detect this in internet explorer by verifying that the mouse is
19759          * down during the mousemove event.  Firefox doesn't give us the
19760          * button state on the mousemove event.
19761          * @method handleMouseMove
19762          * @param {Event} e the event
19763          * @private
19764          * @static
19765          */
19766         handleMouseMove: function(e) {
19767             if (! this.dragCurrent) {
19768                 return true;
19769             }
19770
19771             // var button = e.which || e.button;
19772
19773             // check for IE mouseup outside of page boundary
19774             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
19775                 this.stopEvent(e);
19776                 return this.handleMouseUp(e);
19777             }
19778
19779             if (!this.dragThreshMet) {
19780                 var diffX = Math.abs(this.startX - e.getPageX());
19781                 var diffY = Math.abs(this.startY - e.getPageY());
19782                 if (diffX > this.clickPixelThresh ||
19783                             diffY > this.clickPixelThresh) {
19784                     this.startDrag(this.startX, this.startY);
19785                 }
19786             }
19787
19788             if (this.dragThreshMet) {
19789                 this.dragCurrent.b4Drag(e);
19790                 this.dragCurrent.onDrag(e);
19791                 if(!this.dragCurrent.moveOnly){
19792                     this.fireEvents(e, false);
19793                 }
19794             }
19795
19796             this.stopEvent(e);
19797
19798             return true;
19799         },
19800
19801         /**
19802          * Iterates over all of the DragDrop elements to find ones we are
19803          * hovering over or dropping on
19804          * @method fireEvents
19805          * @param {Event} e the event
19806          * @param {boolean} isDrop is this a drop op or a mouseover op?
19807          * @private
19808          * @static
19809          */
19810         fireEvents: function(e, isDrop) {
19811             var dc = this.dragCurrent;
19812
19813             // If the user did the mouse up outside of the window, we could
19814             // get here even though we have ended the drag.
19815             if (!dc || dc.isLocked()) {
19816                 return;
19817             }
19818
19819             var pt = e.getPoint();
19820
19821             // cache the previous dragOver array
19822             var oldOvers = [];
19823
19824             var outEvts   = [];
19825             var overEvts  = [];
19826             var dropEvts  = [];
19827             var enterEvts = [];
19828
19829             // Check to see if the object(s) we were hovering over is no longer
19830             // being hovered over so we can fire the onDragOut event
19831             for (var i in this.dragOvers) {
19832
19833                 var ddo = this.dragOvers[i];
19834
19835                 if (! this.isTypeOfDD(ddo)) {
19836                     continue;
19837                 }
19838
19839                 if (! this.isOverTarget(pt, ddo, this.mode)) {
19840                     outEvts.push( ddo );
19841                 }
19842
19843                 oldOvers[i] = true;
19844                 delete this.dragOvers[i];
19845             }
19846
19847             for (var sGroup in dc.groups) {
19848
19849                 if ("string" != typeof sGroup) {
19850                     continue;
19851                 }
19852
19853                 for (i in this.ids[sGroup]) {
19854                     var oDD = this.ids[sGroup][i];
19855                     if (! this.isTypeOfDD(oDD)) {
19856                         continue;
19857                     }
19858
19859                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
19860                         if (this.isOverTarget(pt, oDD, this.mode)) {
19861                             // look for drop interactions
19862                             if (isDrop) {
19863                                 dropEvts.push( oDD );
19864                             // look for drag enter and drag over interactions
19865                             } else {
19866
19867                                 // initial drag over: dragEnter fires
19868                                 if (!oldOvers[oDD.id]) {
19869                                     enterEvts.push( oDD );
19870                                 // subsequent drag overs: dragOver fires
19871                                 } else {
19872                                     overEvts.push( oDD );
19873                                 }
19874
19875                                 this.dragOvers[oDD.id] = oDD;
19876                             }
19877                         }
19878                     }
19879                 }
19880             }
19881
19882             if (this.mode) {
19883                 if (outEvts.length) {
19884                     dc.b4DragOut(e, outEvts);
19885                     dc.onDragOut(e, outEvts);
19886                 }
19887
19888                 if (enterEvts.length) {
19889                     dc.onDragEnter(e, enterEvts);
19890                 }
19891
19892                 if (overEvts.length) {
19893                     dc.b4DragOver(e, overEvts);
19894                     dc.onDragOver(e, overEvts);
19895                 }
19896
19897                 if (dropEvts.length) {
19898                     dc.b4DragDrop(e, dropEvts);
19899                     dc.onDragDrop(e, dropEvts);
19900                 }
19901
19902             } else {
19903                 // fire dragout events
19904                 var len = 0;
19905                 for (i=0, len=outEvts.length; i<len; ++i) {
19906                     dc.b4DragOut(e, outEvts[i].id);
19907                     dc.onDragOut(e, outEvts[i].id);
19908                 }
19909
19910                 // fire enter events
19911                 for (i=0,len=enterEvts.length; i<len; ++i) {
19912                     // dc.b4DragEnter(e, oDD.id);
19913                     dc.onDragEnter(e, enterEvts[i].id);
19914                 }
19915
19916                 // fire over events
19917                 for (i=0,len=overEvts.length; i<len; ++i) {
19918                     dc.b4DragOver(e, overEvts[i].id);
19919                     dc.onDragOver(e, overEvts[i].id);
19920                 }
19921
19922                 // fire drop events
19923                 for (i=0, len=dropEvts.length; i<len; ++i) {
19924                     dc.b4DragDrop(e, dropEvts[i].id);
19925                     dc.onDragDrop(e, dropEvts[i].id);
19926                 }
19927
19928             }
19929
19930             // notify about a drop that did not find a target
19931             if (isDrop && !dropEvts.length) {
19932                 dc.onInvalidDrop(e);
19933             }
19934
19935         },
19936
19937         /**
19938          * Helper function for getting the best match from the list of drag
19939          * and drop objects returned by the drag and drop events when we are
19940          * in INTERSECT mode.  It returns either the first object that the
19941          * cursor is over, or the object that has the greatest overlap with
19942          * the dragged element.
19943          * @method getBestMatch
19944          * @param  {DragDrop[]} dds The array of drag and drop objects
19945          * targeted
19946          * @return {DragDrop}       The best single match
19947          * @static
19948          */
19949         getBestMatch: function(dds) {
19950             var winner = null;
19951             // Return null if the input is not what we expect
19952             //if (!dds || !dds.length || dds.length == 0) {
19953                // winner = null;
19954             // If there is only one item, it wins
19955             //} else if (dds.length == 1) {
19956
19957             var len = dds.length;
19958
19959             if (len == 1) {
19960                 winner = dds[0];
19961             } else {
19962                 // Loop through the targeted items
19963                 for (var i=0; i<len; ++i) {
19964                     var dd = dds[i];
19965                     // If the cursor is over the object, it wins.  If the
19966                     // cursor is over multiple matches, the first one we come
19967                     // to wins.
19968                     if (dd.cursorIsOver) {
19969                         winner = dd;
19970                         break;
19971                     // Otherwise the object with the most overlap wins
19972                     } else {
19973                         if (!winner ||
19974                             winner.overlap.getArea() < dd.overlap.getArea()) {
19975                             winner = dd;
19976                         }
19977                     }
19978                 }
19979             }
19980
19981             return winner;
19982         },
19983
19984         /**
19985          * Refreshes the cache of the top-left and bottom-right points of the
19986          * drag and drop objects in the specified group(s).  This is in the
19987          * format that is stored in the drag and drop instance, so typical
19988          * usage is:
19989          * <code>
19990          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
19991          * </code>
19992          * Alternatively:
19993          * <code>
19994          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
19995          * </code>
19996          * @TODO this really should be an indexed array.  Alternatively this
19997          * method could accept both.
19998          * @method refreshCache
19999          * @param {Object} groups an associative array of groups to refresh
20000          * @static
20001          */
20002         refreshCache: function(groups) {
20003             for (var sGroup in groups) {
20004                 if ("string" != typeof sGroup) {
20005                     continue;
20006                 }
20007                 for (var i in this.ids[sGroup]) {
20008                     var oDD = this.ids[sGroup][i];
20009
20010                     if (this.isTypeOfDD(oDD)) {
20011                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20012                         var loc = this.getLocation(oDD);
20013                         if (loc) {
20014                             this.locationCache[oDD.id] = loc;
20015                         } else {
20016                             delete this.locationCache[oDD.id];
20017                             // this will unregister the drag and drop object if
20018                             // the element is not in a usable state
20019                             // oDD.unreg();
20020                         }
20021                     }
20022                 }
20023             }
20024         },
20025
20026         /**
20027          * This checks to make sure an element exists and is in the DOM.  The
20028          * main purpose is to handle cases where innerHTML is used to remove
20029          * drag and drop objects from the DOM.  IE provides an 'unspecified
20030          * error' when trying to access the offsetParent of such an element
20031          * @method verifyEl
20032          * @param {HTMLElement} el the element to check
20033          * @return {boolean} true if the element looks usable
20034          * @static
20035          */
20036         verifyEl: function(el) {
20037             if (el) {
20038                 var parent;
20039                 if(Roo.isIE){
20040                     try{
20041                         parent = el.offsetParent;
20042                     }catch(e){}
20043                 }else{
20044                     parent = el.offsetParent;
20045                 }
20046                 if (parent) {
20047                     return true;
20048                 }
20049             }
20050
20051             return false;
20052         },
20053
20054         /**
20055          * Returns a Region object containing the drag and drop element's position
20056          * and size, including the padding configured for it
20057          * @method getLocation
20058          * @param {DragDrop} oDD the drag and drop object to get the
20059          *                       location for
20060          * @return {Roo.lib.Region} a Region object representing the total area
20061          *                             the element occupies, including any padding
20062          *                             the instance is configured for.
20063          * @static
20064          */
20065         getLocation: function(oDD) {
20066             if (! this.isTypeOfDD(oDD)) {
20067                 return null;
20068             }
20069
20070             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20071
20072             try {
20073                 pos= Roo.lib.Dom.getXY(el);
20074             } catch (e) { }
20075
20076             if (!pos) {
20077                 return null;
20078             }
20079
20080             x1 = pos[0];
20081             x2 = x1 + el.offsetWidth;
20082             y1 = pos[1];
20083             y2 = y1 + el.offsetHeight;
20084
20085             t = y1 - oDD.padding[0];
20086             r = x2 + oDD.padding[1];
20087             b = y2 + oDD.padding[2];
20088             l = x1 - oDD.padding[3];
20089
20090             return new Roo.lib.Region( t, r, b, l );
20091         },
20092
20093         /**
20094          * Checks the cursor location to see if it over the target
20095          * @method isOverTarget
20096          * @param {Roo.lib.Point} pt The point to evaluate
20097          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20098          * @return {boolean} true if the mouse is over the target
20099          * @private
20100          * @static
20101          */
20102         isOverTarget: function(pt, oTarget, intersect) {
20103             // use cache if available
20104             var loc = this.locationCache[oTarget.id];
20105             if (!loc || !this.useCache) {
20106                 loc = this.getLocation(oTarget);
20107                 this.locationCache[oTarget.id] = loc;
20108
20109             }
20110
20111             if (!loc) {
20112                 return false;
20113             }
20114
20115             oTarget.cursorIsOver = loc.contains( pt );
20116
20117             // DragDrop is using this as a sanity check for the initial mousedown
20118             // in this case we are done.  In POINT mode, if the drag obj has no
20119             // contraints, we are also done. Otherwise we need to evaluate the
20120             // location of the target as related to the actual location of the
20121             // dragged element.
20122             var dc = this.dragCurrent;
20123             if (!dc || !dc.getTargetCoord ||
20124                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20125                 return oTarget.cursorIsOver;
20126             }
20127
20128             oTarget.overlap = null;
20129
20130             // Get the current location of the drag element, this is the
20131             // location of the mouse event less the delta that represents
20132             // where the original mousedown happened on the element.  We
20133             // need to consider constraints and ticks as well.
20134             var pos = dc.getTargetCoord(pt.x, pt.y);
20135
20136             var el = dc.getDragEl();
20137             var curRegion = new Roo.lib.Region( pos.y,
20138                                                    pos.x + el.offsetWidth,
20139                                                    pos.y + el.offsetHeight,
20140                                                    pos.x );
20141
20142             var overlap = curRegion.intersect(loc);
20143
20144             if (overlap) {
20145                 oTarget.overlap = overlap;
20146                 return (intersect) ? true : oTarget.cursorIsOver;
20147             } else {
20148                 return false;
20149             }
20150         },
20151
20152         /**
20153          * unload event handler
20154          * @method _onUnload
20155          * @private
20156          * @static
20157          */
20158         _onUnload: function(e, me) {
20159             Roo.dd.DragDropMgr.unregAll();
20160         },
20161
20162         /**
20163          * Cleans up the drag and drop events and objects.
20164          * @method unregAll
20165          * @private
20166          * @static
20167          */
20168         unregAll: function() {
20169
20170             if (this.dragCurrent) {
20171                 this.stopDrag();
20172                 this.dragCurrent = null;
20173             }
20174
20175             this._execOnAll("unreg", []);
20176
20177             for (i in this.elementCache) {
20178                 delete this.elementCache[i];
20179             }
20180
20181             this.elementCache = {};
20182             this.ids = {};
20183         },
20184
20185         /**
20186          * A cache of DOM elements
20187          * @property elementCache
20188          * @private
20189          * @static
20190          */
20191         elementCache: {},
20192
20193         /**
20194          * Get the wrapper for the DOM element specified
20195          * @method getElWrapper
20196          * @param {String} id the id of the element to get
20197          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20198          * @private
20199          * @deprecated This wrapper isn't that useful
20200          * @static
20201          */
20202         getElWrapper: function(id) {
20203             var oWrapper = this.elementCache[id];
20204             if (!oWrapper || !oWrapper.el) {
20205                 oWrapper = this.elementCache[id] =
20206                     new this.ElementWrapper(Roo.getDom(id));
20207             }
20208             return oWrapper;
20209         },
20210
20211         /**
20212          * Returns the actual DOM element
20213          * @method getElement
20214          * @param {String} id the id of the elment to get
20215          * @return {Object} The element
20216          * @deprecated use Roo.getDom instead
20217          * @static
20218          */
20219         getElement: function(id) {
20220             return Roo.getDom(id);
20221         },
20222
20223         /**
20224          * Returns the style property for the DOM element (i.e.,
20225          * document.getElById(id).style)
20226          * @method getCss
20227          * @param {String} id the id of the elment to get
20228          * @return {Object} The style property of the element
20229          * @deprecated use Roo.getDom instead
20230          * @static
20231          */
20232         getCss: function(id) {
20233             var el = Roo.getDom(id);
20234             return (el) ? el.style : null;
20235         },
20236
20237         /**
20238          * Inner class for cached elements
20239          * @class DragDropMgr.ElementWrapper
20240          * @for DragDropMgr
20241          * @private
20242          * @deprecated
20243          */
20244         ElementWrapper: function(el) {
20245                 /**
20246                  * The element
20247                  * @property el
20248                  */
20249                 this.el = el || null;
20250                 /**
20251                  * The element id
20252                  * @property id
20253                  */
20254                 this.id = this.el && el.id;
20255                 /**
20256                  * A reference to the style property
20257                  * @property css
20258                  */
20259                 this.css = this.el && el.style;
20260             },
20261
20262         /**
20263          * Returns the X position of an html element
20264          * @method getPosX
20265          * @param el the element for which to get the position
20266          * @return {int} the X coordinate
20267          * @for DragDropMgr
20268          * @deprecated use Roo.lib.Dom.getX instead
20269          * @static
20270          */
20271         getPosX: function(el) {
20272             return Roo.lib.Dom.getX(el);
20273         },
20274
20275         /**
20276          * Returns the Y position of an html element
20277          * @method getPosY
20278          * @param el the element for which to get the position
20279          * @return {int} the Y coordinate
20280          * @deprecated use Roo.lib.Dom.getY instead
20281          * @static
20282          */
20283         getPosY: function(el) {
20284             return Roo.lib.Dom.getY(el);
20285         },
20286
20287         /**
20288          * Swap two nodes.  In IE, we use the native method, for others we
20289          * emulate the IE behavior
20290          * @method swapNode
20291          * @param n1 the first node to swap
20292          * @param n2 the other node to swap
20293          * @static
20294          */
20295         swapNode: function(n1, n2) {
20296             if (n1.swapNode) {
20297                 n1.swapNode(n2);
20298             } else {
20299                 var p = n2.parentNode;
20300                 var s = n2.nextSibling;
20301
20302                 if (s == n1) {
20303                     p.insertBefore(n1, n2);
20304                 } else if (n2 == n1.nextSibling) {
20305                     p.insertBefore(n2, n1);
20306                 } else {
20307                     n1.parentNode.replaceChild(n2, n1);
20308                     p.insertBefore(n1, s);
20309                 }
20310             }
20311         },
20312
20313         /**
20314          * Returns the current scroll position
20315          * @method getScroll
20316          * @private
20317          * @static
20318          */
20319         getScroll: function () {
20320             var t, l, dde=document.documentElement, db=document.body;
20321             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20322                 t = dde.scrollTop;
20323                 l = dde.scrollLeft;
20324             } else if (db) {
20325                 t = db.scrollTop;
20326                 l = db.scrollLeft;
20327             } else {
20328
20329             }
20330             return { top: t, left: l };
20331         },
20332
20333         /**
20334          * Returns the specified element style property
20335          * @method getStyle
20336          * @param {HTMLElement} el          the element
20337          * @param {string}      styleProp   the style property
20338          * @return {string} The value of the style property
20339          * @deprecated use Roo.lib.Dom.getStyle
20340          * @static
20341          */
20342         getStyle: function(el, styleProp) {
20343             return Roo.fly(el).getStyle(styleProp);
20344         },
20345
20346         /**
20347          * Gets the scrollTop
20348          * @method getScrollTop
20349          * @return {int} the document's scrollTop
20350          * @static
20351          */
20352         getScrollTop: function () { return this.getScroll().top; },
20353
20354         /**
20355          * Gets the scrollLeft
20356          * @method getScrollLeft
20357          * @return {int} the document's scrollTop
20358          * @static
20359          */
20360         getScrollLeft: function () { return this.getScroll().left; },
20361
20362         /**
20363          * Sets the x/y position of an element to the location of the
20364          * target element.
20365          * @method moveToEl
20366          * @param {HTMLElement} moveEl      The element to move
20367          * @param {HTMLElement} targetEl    The position reference element
20368          * @static
20369          */
20370         moveToEl: function (moveEl, targetEl) {
20371             var aCoord = Roo.lib.Dom.getXY(targetEl);
20372             Roo.lib.Dom.setXY(moveEl, aCoord);
20373         },
20374
20375         /**
20376          * Numeric array sort function
20377          * @method numericSort
20378          * @static
20379          */
20380         numericSort: function(a, b) { return (a - b); },
20381
20382         /**
20383          * Internal counter
20384          * @property _timeoutCount
20385          * @private
20386          * @static
20387          */
20388         _timeoutCount: 0,
20389
20390         /**
20391          * Trying to make the load order less important.  Without this we get
20392          * an error if this file is loaded before the Event Utility.
20393          * @method _addListeners
20394          * @private
20395          * @static
20396          */
20397         _addListeners: function() {
20398             var DDM = Roo.dd.DDM;
20399             if ( Roo.lib.Event && document ) {
20400                 DDM._onLoad();
20401             } else {
20402                 if (DDM._timeoutCount > 2000) {
20403                 } else {
20404                     setTimeout(DDM._addListeners, 10);
20405                     if (document && document.body) {
20406                         DDM._timeoutCount += 1;
20407                     }
20408                 }
20409             }
20410         },
20411
20412         /**
20413          * Recursively searches the immediate parent and all child nodes for
20414          * the handle element in order to determine wheter or not it was
20415          * clicked.
20416          * @method handleWasClicked
20417          * @param node the html element to inspect
20418          * @static
20419          */
20420         handleWasClicked: function(node, id) {
20421             if (this.isHandle(id, node.id)) {
20422                 return true;
20423             } else {
20424                 // check to see if this is a text node child of the one we want
20425                 var p = node.parentNode;
20426
20427                 while (p) {
20428                     if (this.isHandle(id, p.id)) {
20429                         return true;
20430                     } else {
20431                         p = p.parentNode;
20432                     }
20433                 }
20434             }
20435
20436             return false;
20437         }
20438
20439     };
20440
20441 }();
20442
20443 // shorter alias, save a few bytes
20444 Roo.dd.DDM = Roo.dd.DragDropMgr;
20445 Roo.dd.DDM._addListeners();
20446
20447 }/*
20448  * Based on:
20449  * Ext JS Library 1.1.1
20450  * Copyright(c) 2006-2007, Ext JS, LLC.
20451  *
20452  * Originally Released Under LGPL - original licence link has changed is not relivant.
20453  *
20454  * Fork - LGPL
20455  * <script type="text/javascript">
20456  */
20457
20458 /**
20459  * @class Roo.dd.DD
20460  * A DragDrop implementation where the linked element follows the
20461  * mouse cursor during a drag.
20462  * @extends Roo.dd.DragDrop
20463  * @constructor
20464  * @param {String} id the id of the linked element
20465  * @param {String} sGroup the group of related DragDrop items
20466  * @param {object} config an object containing configurable attributes
20467  *                Valid properties for DD:
20468  *                    scroll
20469  */
20470 Roo.dd.DD = function(id, sGroup, config) {
20471     if (id) {
20472         this.init(id, sGroup, config);
20473     }
20474 };
20475
20476 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
20477
20478     /**
20479      * When set to true, the utility automatically tries to scroll the browser
20480      * window wehn a drag and drop element is dragged near the viewport boundary.
20481      * Defaults to true.
20482      * @property scroll
20483      * @type boolean
20484      */
20485     scroll: true,
20486
20487     /**
20488      * Sets the pointer offset to the distance between the linked element's top
20489      * left corner and the location the element was clicked
20490      * @method autoOffset
20491      * @param {int} iPageX the X coordinate of the click
20492      * @param {int} iPageY the Y coordinate of the click
20493      */
20494     autoOffset: function(iPageX, iPageY) {
20495         var x = iPageX - this.startPageX;
20496         var y = iPageY - this.startPageY;
20497         this.setDelta(x, y);
20498     },
20499
20500     /**
20501      * Sets the pointer offset.  You can call this directly to force the
20502      * offset to be in a particular location (e.g., pass in 0,0 to set it
20503      * to the center of the object)
20504      * @method setDelta
20505      * @param {int} iDeltaX the distance from the left
20506      * @param {int} iDeltaY the distance from the top
20507      */
20508     setDelta: function(iDeltaX, iDeltaY) {
20509         this.deltaX = iDeltaX;
20510         this.deltaY = iDeltaY;
20511     },
20512
20513     /**
20514      * Sets the drag element to the location of the mousedown or click event,
20515      * maintaining the cursor location relative to the location on the element
20516      * that was clicked.  Override this if you want to place the element in a
20517      * location other than where the cursor is.
20518      * @method setDragElPos
20519      * @param {int} iPageX the X coordinate of the mousedown or drag event
20520      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20521      */
20522     setDragElPos: function(iPageX, iPageY) {
20523         // the first time we do this, we are going to check to make sure
20524         // the element has css positioning
20525
20526         var el = this.getDragEl();
20527         this.alignElWithMouse(el, iPageX, iPageY);
20528     },
20529
20530     /**
20531      * Sets the element to the location of the mousedown or click event,
20532      * maintaining the cursor location relative to the location on the element
20533      * that was clicked.  Override this if you want to place the element in a
20534      * location other than where the cursor is.
20535      * @method alignElWithMouse
20536      * @param {HTMLElement} el the element to move
20537      * @param {int} iPageX the X coordinate of the mousedown or drag event
20538      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20539      */
20540     alignElWithMouse: function(el, iPageX, iPageY) {
20541         var oCoord = this.getTargetCoord(iPageX, iPageY);
20542         var fly = el.dom ? el : Roo.fly(el);
20543         if (!this.deltaSetXY) {
20544             var aCoord = [oCoord.x, oCoord.y];
20545             fly.setXY(aCoord);
20546             var newLeft = fly.getLeft(true);
20547             var newTop  = fly.getTop(true);
20548             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
20549         } else {
20550             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
20551         }
20552
20553         this.cachePosition(oCoord.x, oCoord.y);
20554         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
20555         return oCoord;
20556     },
20557
20558     /**
20559      * Saves the most recent position so that we can reset the constraints and
20560      * tick marks on-demand.  We need to know this so that we can calculate the
20561      * number of pixels the element is offset from its original position.
20562      * @method cachePosition
20563      * @param iPageX the current x position (optional, this just makes it so we
20564      * don't have to look it up again)
20565      * @param iPageY the current y position (optional, this just makes it so we
20566      * don't have to look it up again)
20567      */
20568     cachePosition: function(iPageX, iPageY) {
20569         if (iPageX) {
20570             this.lastPageX = iPageX;
20571             this.lastPageY = iPageY;
20572         } else {
20573             var aCoord = Roo.lib.Dom.getXY(this.getEl());
20574             this.lastPageX = aCoord[0];
20575             this.lastPageY = aCoord[1];
20576         }
20577     },
20578
20579     /**
20580      * Auto-scroll the window if the dragged object has been moved beyond the
20581      * visible window boundary.
20582      * @method autoScroll
20583      * @param {int} x the drag element's x position
20584      * @param {int} y the drag element's y position
20585      * @param {int} h the height of the drag element
20586      * @param {int} w the width of the drag element
20587      * @private
20588      */
20589     autoScroll: function(x, y, h, w) {
20590
20591         if (this.scroll) {
20592             // The client height
20593             var clientH = Roo.lib.Dom.getViewWidth();
20594
20595             // The client width
20596             var clientW = Roo.lib.Dom.getViewHeight();
20597
20598             // The amt scrolled down
20599             var st = this.DDM.getScrollTop();
20600
20601             // The amt scrolled right
20602             var sl = this.DDM.getScrollLeft();
20603
20604             // Location of the bottom of the element
20605             var bot = h + y;
20606
20607             // Location of the right of the element
20608             var right = w + x;
20609
20610             // The distance from the cursor to the bottom of the visible area,
20611             // adjusted so that we don't scroll if the cursor is beyond the
20612             // element drag constraints
20613             var toBot = (clientH + st - y - this.deltaY);
20614
20615             // The distance from the cursor to the right of the visible area
20616             var toRight = (clientW + sl - x - this.deltaX);
20617
20618
20619             // How close to the edge the cursor must be before we scroll
20620             // var thresh = (document.all) ? 100 : 40;
20621             var thresh = 40;
20622
20623             // How many pixels to scroll per autoscroll op.  This helps to reduce
20624             // clunky scrolling. IE is more sensitive about this ... it needs this
20625             // value to be higher.
20626             var scrAmt = (document.all) ? 80 : 30;
20627
20628             // Scroll down if we are near the bottom of the visible page and the
20629             // obj extends below the crease
20630             if ( bot > clientH && toBot < thresh ) {
20631                 window.scrollTo(sl, st + scrAmt);
20632             }
20633
20634             // Scroll up if the window is scrolled down and the top of the object
20635             // goes above the top border
20636             if ( y < st && st > 0 && y - st < thresh ) {
20637                 window.scrollTo(sl, st - scrAmt);
20638             }
20639
20640             // Scroll right if the obj is beyond the right border and the cursor is
20641             // near the border.
20642             if ( right > clientW && toRight < thresh ) {
20643                 window.scrollTo(sl + scrAmt, st);
20644             }
20645
20646             // Scroll left if the window has been scrolled to the right and the obj
20647             // extends past the left border
20648             if ( x < sl && sl > 0 && x - sl < thresh ) {
20649                 window.scrollTo(sl - scrAmt, st);
20650             }
20651         }
20652     },
20653
20654     /**
20655      * Finds the location the element should be placed if we want to move
20656      * it to where the mouse location less the click offset would place us.
20657      * @method getTargetCoord
20658      * @param {int} iPageX the X coordinate of the click
20659      * @param {int} iPageY the Y coordinate of the click
20660      * @return an object that contains the coordinates (Object.x and Object.y)
20661      * @private
20662      */
20663     getTargetCoord: function(iPageX, iPageY) {
20664
20665
20666         var x = iPageX - this.deltaX;
20667         var y = iPageY - this.deltaY;
20668
20669         if (this.constrainX) {
20670             if (x < this.minX) { x = this.minX; }
20671             if (x > this.maxX) { x = this.maxX; }
20672         }
20673
20674         if (this.constrainY) {
20675             if (y < this.minY) { y = this.minY; }
20676             if (y > this.maxY) { y = this.maxY; }
20677         }
20678
20679         x = this.getTick(x, this.xTicks);
20680         y = this.getTick(y, this.yTicks);
20681
20682
20683         return {x:x, y:y};
20684     },
20685
20686     /*
20687      * Sets up config options specific to this class. Overrides
20688      * Roo.dd.DragDrop, but all versions of this method through the
20689      * inheritance chain are called
20690      */
20691     applyConfig: function() {
20692         Roo.dd.DD.superclass.applyConfig.call(this);
20693         this.scroll = (this.config.scroll !== false);
20694     },
20695
20696     /*
20697      * Event that fires prior to the onMouseDown event.  Overrides
20698      * Roo.dd.DragDrop.
20699      */
20700     b4MouseDown: function(e) {
20701         // this.resetConstraints();
20702         this.autoOffset(e.getPageX(),
20703                             e.getPageY());
20704     },
20705
20706     /*
20707      * Event that fires prior to the onDrag event.  Overrides
20708      * Roo.dd.DragDrop.
20709      */
20710     b4Drag: function(e) {
20711         this.setDragElPos(e.getPageX(),
20712                             e.getPageY());
20713     },
20714
20715     toString: function() {
20716         return ("DD " + this.id);
20717     }
20718
20719     //////////////////////////////////////////////////////////////////////////
20720     // Debugging ygDragDrop events that can be overridden
20721     //////////////////////////////////////////////////////////////////////////
20722     /*
20723     startDrag: function(x, y) {
20724     },
20725
20726     onDrag: function(e) {
20727     },
20728
20729     onDragEnter: function(e, id) {
20730     },
20731
20732     onDragOver: function(e, id) {
20733     },
20734
20735     onDragOut: function(e, id) {
20736     },
20737
20738     onDragDrop: function(e, id) {
20739     },
20740
20741     endDrag: function(e) {
20742     }
20743
20744     */
20745
20746 });/*
20747  * Based on:
20748  * Ext JS Library 1.1.1
20749  * Copyright(c) 2006-2007, Ext JS, LLC.
20750  *
20751  * Originally Released Under LGPL - original licence link has changed is not relivant.
20752  *
20753  * Fork - LGPL
20754  * <script type="text/javascript">
20755  */
20756
20757 /**
20758  * @class Roo.dd.DDProxy
20759  * A DragDrop implementation that inserts an empty, bordered div into
20760  * the document that follows the cursor during drag operations.  At the time of
20761  * the click, the frame div is resized to the dimensions of the linked html
20762  * element, and moved to the exact location of the linked element.
20763  *
20764  * References to the "frame" element refer to the single proxy element that
20765  * was created to be dragged in place of all DDProxy elements on the
20766  * page.
20767  *
20768  * @extends Roo.dd.DD
20769  * @constructor
20770  * @param {String} id the id of the linked html element
20771  * @param {String} sGroup the group of related DragDrop objects
20772  * @param {object} config an object containing configurable attributes
20773  *                Valid properties for DDProxy in addition to those in DragDrop:
20774  *                   resizeFrame, centerFrame, dragElId
20775  */
20776 Roo.dd.DDProxy = function(id, sGroup, config) {
20777     if (id) {
20778         this.init(id, sGroup, config);
20779         this.initFrame();
20780     }
20781 };
20782
20783 /**
20784  * The default drag frame div id
20785  * @property Roo.dd.DDProxy.dragElId
20786  * @type String
20787  * @static
20788  */
20789 Roo.dd.DDProxy.dragElId = "ygddfdiv";
20790
20791 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
20792
20793     /**
20794      * By default we resize the drag frame to be the same size as the element
20795      * we want to drag (this is to get the frame effect).  We can turn it off
20796      * if we want a different behavior.
20797      * @property resizeFrame
20798      * @type boolean
20799      */
20800     resizeFrame: true,
20801
20802     /**
20803      * By default the frame is positioned exactly where the drag element is, so
20804      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
20805      * you do not have constraints on the obj is to have the drag frame centered
20806      * around the cursor.  Set centerFrame to true for this effect.
20807      * @property centerFrame
20808      * @type boolean
20809      */
20810     centerFrame: false,
20811
20812     /**
20813      * Creates the proxy element if it does not yet exist
20814      * @method createFrame
20815      */
20816     createFrame: function() {
20817         var self = this;
20818         var body = document.body;
20819
20820         if (!body || !body.firstChild) {
20821             setTimeout( function() { self.createFrame(); }, 50 );
20822             return;
20823         }
20824
20825         var div = this.getDragEl();
20826
20827         if (!div) {
20828             div    = document.createElement("div");
20829             div.id = this.dragElId;
20830             var s  = div.style;
20831
20832             s.position   = "absolute";
20833             s.visibility = "hidden";
20834             s.cursor     = "move";
20835             s.border     = "2px solid #aaa";
20836             s.zIndex     = 999;
20837
20838             // appendChild can blow up IE if invoked prior to the window load event
20839             // while rendering a table.  It is possible there are other scenarios
20840             // that would cause this to happen as well.
20841             body.insertBefore(div, body.firstChild);
20842         }
20843     },
20844
20845     /**
20846      * Initialization for the drag frame element.  Must be called in the
20847      * constructor of all subclasses
20848      * @method initFrame
20849      */
20850     initFrame: function() {
20851         this.createFrame();
20852     },
20853
20854     applyConfig: function() {
20855         Roo.dd.DDProxy.superclass.applyConfig.call(this);
20856
20857         this.resizeFrame = (this.config.resizeFrame !== false);
20858         this.centerFrame = (this.config.centerFrame);
20859         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
20860     },
20861
20862     /**
20863      * Resizes the drag frame to the dimensions of the clicked object, positions
20864      * it over the object, and finally displays it
20865      * @method showFrame
20866      * @param {int} iPageX X click position
20867      * @param {int} iPageY Y click position
20868      * @private
20869      */
20870     showFrame: function(iPageX, iPageY) {
20871         var el = this.getEl();
20872         var dragEl = this.getDragEl();
20873         var s = dragEl.style;
20874
20875         this._resizeProxy();
20876
20877         if (this.centerFrame) {
20878             this.setDelta( Math.round(parseInt(s.width,  10)/2),
20879                            Math.round(parseInt(s.height, 10)/2) );
20880         }
20881
20882         this.setDragElPos(iPageX, iPageY);
20883
20884         Roo.fly(dragEl).show();
20885     },
20886
20887     /**
20888      * The proxy is automatically resized to the dimensions of the linked
20889      * element when a drag is initiated, unless resizeFrame is set to false
20890      * @method _resizeProxy
20891      * @private
20892      */
20893     _resizeProxy: function() {
20894         if (this.resizeFrame) {
20895             var el = this.getEl();
20896             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
20897         }
20898     },
20899
20900     // overrides Roo.dd.DragDrop
20901     b4MouseDown: function(e) {
20902         var x = e.getPageX();
20903         var y = e.getPageY();
20904         this.autoOffset(x, y);
20905         this.setDragElPos(x, y);
20906     },
20907
20908     // overrides Roo.dd.DragDrop
20909     b4StartDrag: function(x, y) {
20910         // show the drag frame
20911         this.showFrame(x, y);
20912     },
20913
20914     // overrides Roo.dd.DragDrop
20915     b4EndDrag: function(e) {
20916         Roo.fly(this.getDragEl()).hide();
20917     },
20918
20919     // overrides Roo.dd.DragDrop
20920     // By default we try to move the element to the last location of the frame.
20921     // This is so that the default behavior mirrors that of Roo.dd.DD.
20922     endDrag: function(e) {
20923
20924         var lel = this.getEl();
20925         var del = this.getDragEl();
20926
20927         // Show the drag frame briefly so we can get its position
20928         del.style.visibility = "";
20929
20930         this.beforeMove();
20931         // Hide the linked element before the move to get around a Safari
20932         // rendering bug.
20933         lel.style.visibility = "hidden";
20934         Roo.dd.DDM.moveToEl(lel, del);
20935         del.style.visibility = "hidden";
20936         lel.style.visibility = "";
20937
20938         this.afterDrag();
20939     },
20940
20941     beforeMove : function(){
20942
20943     },
20944
20945     afterDrag : function(){
20946
20947     },
20948
20949     toString: function() {
20950         return ("DDProxy " + this.id);
20951     }
20952
20953 });
20954 /*
20955  * Based on:
20956  * Ext JS Library 1.1.1
20957  * Copyright(c) 2006-2007, Ext JS, LLC.
20958  *
20959  * Originally Released Under LGPL - original licence link has changed is not relivant.
20960  *
20961  * Fork - LGPL
20962  * <script type="text/javascript">
20963  */
20964
20965  /**
20966  * @class Roo.dd.DDTarget
20967  * A DragDrop implementation that does not move, but can be a drop
20968  * target.  You would get the same result by simply omitting implementation
20969  * for the event callbacks, but this way we reduce the processing cost of the
20970  * event listener and the callbacks.
20971  * @extends Roo.dd.DragDrop
20972  * @constructor
20973  * @param {String} id the id of the element that is a drop target
20974  * @param {String} sGroup the group of related DragDrop objects
20975  * @param {object} config an object containing configurable attributes
20976  *                 Valid properties for DDTarget in addition to those in
20977  *                 DragDrop:
20978  *                    none
20979  */
20980 Roo.dd.DDTarget = function(id, sGroup, config) {
20981     if (id) {
20982         this.initTarget(id, sGroup, config);
20983     }
20984     if (config.listeners || config.events) { 
20985        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
20986             listeners : config.listeners || {}, 
20987             events : config.events || {} 
20988         });    
20989     }
20990 };
20991
20992 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
20993 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
20994     toString: function() {
20995         return ("DDTarget " + this.id);
20996     }
20997 });
20998 /*
20999  * Based on:
21000  * Ext JS Library 1.1.1
21001  * Copyright(c) 2006-2007, Ext JS, LLC.
21002  *
21003  * Originally Released Under LGPL - original licence link has changed is not relivant.
21004  *
21005  * Fork - LGPL
21006  * <script type="text/javascript">
21007  */
21008  
21009
21010 /**
21011  * @class Roo.dd.ScrollManager
21012  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21013  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21014  * @singleton
21015  */
21016 Roo.dd.ScrollManager = function(){
21017     var ddm = Roo.dd.DragDropMgr;
21018     var els = {};
21019     var dragEl = null;
21020     var proc = {};
21021     
21022     
21023     
21024     var onStop = function(e){
21025         dragEl = null;
21026         clearProc();
21027     };
21028     
21029     var triggerRefresh = function(){
21030         if(ddm.dragCurrent){
21031              ddm.refreshCache(ddm.dragCurrent.groups);
21032         }
21033     };
21034     
21035     var doScroll = function(){
21036         if(ddm.dragCurrent){
21037             var dds = Roo.dd.ScrollManager;
21038             if(!dds.animate){
21039                 if(proc.el.scroll(proc.dir, dds.increment)){
21040                     triggerRefresh();
21041                 }
21042             }else{
21043                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21044             }
21045         }
21046     };
21047     
21048     var clearProc = function(){
21049         if(proc.id){
21050             clearInterval(proc.id);
21051         }
21052         proc.id = 0;
21053         proc.el = null;
21054         proc.dir = "";
21055     };
21056     
21057     var startProc = function(el, dir){
21058          Roo.log('scroll startproc');
21059         clearProc();
21060         proc.el = el;
21061         proc.dir = dir;
21062         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21063     };
21064     
21065     var onFire = function(e, isDrop){
21066        
21067         if(isDrop || !ddm.dragCurrent){ return; }
21068         var dds = Roo.dd.ScrollManager;
21069         if(!dragEl || dragEl != ddm.dragCurrent){
21070             dragEl = ddm.dragCurrent;
21071             // refresh regions on drag start
21072             dds.refreshCache();
21073         }
21074         
21075         var xy = Roo.lib.Event.getXY(e);
21076         var pt = new Roo.lib.Point(xy[0], xy[1]);
21077         for(var id in els){
21078             var el = els[id], r = el._region;
21079             if(r && r.contains(pt) && el.isScrollable()){
21080                 if(r.bottom - pt.y <= dds.thresh){
21081                     if(proc.el != el){
21082                         startProc(el, "down");
21083                     }
21084                     return;
21085                 }else if(r.right - pt.x <= dds.thresh){
21086                     if(proc.el != el){
21087                         startProc(el, "left");
21088                     }
21089                     return;
21090                 }else if(pt.y - r.top <= dds.thresh){
21091                     if(proc.el != el){
21092                         startProc(el, "up");
21093                     }
21094                     return;
21095                 }else if(pt.x - r.left <= dds.thresh){
21096                     if(proc.el != el){
21097                         startProc(el, "right");
21098                     }
21099                     return;
21100                 }
21101             }
21102         }
21103         clearProc();
21104     };
21105     
21106     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21107     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21108     
21109     return {
21110         /**
21111          * Registers new overflow element(s) to auto scroll
21112          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21113          */
21114         register : function(el){
21115             if(el instanceof Array){
21116                 for(var i = 0, len = el.length; i < len; i++) {
21117                         this.register(el[i]);
21118                 }
21119             }else{
21120                 el = Roo.get(el);
21121                 els[el.id] = el;
21122             }
21123             Roo.dd.ScrollManager.els = els;
21124         },
21125         
21126         /**
21127          * Unregisters overflow element(s) so they are no longer scrolled
21128          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21129          */
21130         unregister : function(el){
21131             if(el instanceof Array){
21132                 for(var i = 0, len = el.length; i < len; i++) {
21133                         this.unregister(el[i]);
21134                 }
21135             }else{
21136                 el = Roo.get(el);
21137                 delete els[el.id];
21138             }
21139         },
21140         
21141         /**
21142          * The number of pixels from the edge of a container the pointer needs to be to 
21143          * trigger scrolling (defaults to 25)
21144          * @type Number
21145          */
21146         thresh : 25,
21147         
21148         /**
21149          * The number of pixels to scroll in each scroll increment (defaults to 50)
21150          * @type Number
21151          */
21152         increment : 100,
21153         
21154         /**
21155          * The frequency of scrolls in milliseconds (defaults to 500)
21156          * @type Number
21157          */
21158         frequency : 500,
21159         
21160         /**
21161          * True to animate the scroll (defaults to true)
21162          * @type Boolean
21163          */
21164         animate: true,
21165         
21166         /**
21167          * The animation duration in seconds - 
21168          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21169          * @type Number
21170          */
21171         animDuration: .4,
21172         
21173         /**
21174          * Manually trigger a cache refresh.
21175          */
21176         refreshCache : function(){
21177             for(var id in els){
21178                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21179                     els[id]._region = els[id].getRegion();
21180                 }
21181             }
21182         }
21183     };
21184 }();/*
21185  * Based on:
21186  * Ext JS Library 1.1.1
21187  * Copyright(c) 2006-2007, Ext JS, LLC.
21188  *
21189  * Originally Released Under LGPL - original licence link has changed is not relivant.
21190  *
21191  * Fork - LGPL
21192  * <script type="text/javascript">
21193  */
21194  
21195
21196 /**
21197  * @class Roo.dd.Registry
21198  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21199  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21200  * @singleton
21201  */
21202 Roo.dd.Registry = function(){
21203     var elements = {}; 
21204     var handles = {}; 
21205     var autoIdSeed = 0;
21206
21207     var getId = function(el, autogen){
21208         if(typeof el == "string"){
21209             return el;
21210         }
21211         var id = el.id;
21212         if(!id && autogen !== false){
21213             id = "roodd-" + (++autoIdSeed);
21214             el.id = id;
21215         }
21216         return id;
21217     };
21218     
21219     return {
21220     /**
21221      * Register a drag drop element
21222      * @param {String|HTMLElement} element The id or DOM node to register
21223      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21224      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21225      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21226      * populated in the data object (if applicable):
21227      * <pre>
21228 Value      Description<br />
21229 ---------  ------------------------------------------<br />
21230 handles    Array of DOM nodes that trigger dragging<br />
21231            for the element being registered<br />
21232 isHandle   True if the element passed in triggers<br />
21233            dragging itself, else false
21234 </pre>
21235      */
21236         register : function(el, data){
21237             data = data || {};
21238             if(typeof el == "string"){
21239                 el = document.getElementById(el);
21240             }
21241             data.ddel = el;
21242             elements[getId(el)] = data;
21243             if(data.isHandle !== false){
21244                 handles[data.ddel.id] = data;
21245             }
21246             if(data.handles){
21247                 var hs = data.handles;
21248                 for(var i = 0, len = hs.length; i < len; i++){
21249                         handles[getId(hs[i])] = data;
21250                 }
21251             }
21252         },
21253
21254     /**
21255      * Unregister a drag drop element
21256      * @param {String|HTMLElement}  element The id or DOM node to unregister
21257      */
21258         unregister : function(el){
21259             var id = getId(el, false);
21260             var data = elements[id];
21261             if(data){
21262                 delete elements[id];
21263                 if(data.handles){
21264                     var hs = data.handles;
21265                     for(var i = 0, len = hs.length; i < len; i++){
21266                         delete handles[getId(hs[i], false)];
21267                     }
21268                 }
21269             }
21270         },
21271
21272     /**
21273      * Returns the handle registered for a DOM Node by id
21274      * @param {String|HTMLElement} id The DOM node or id to look up
21275      * @return {Object} handle The custom handle data
21276      */
21277         getHandle : function(id){
21278             if(typeof id != "string"){ // must be element?
21279                 id = id.id;
21280             }
21281             return handles[id];
21282         },
21283
21284     /**
21285      * Returns the handle that is registered for the DOM node that is the target of the event
21286      * @param {Event} e The event
21287      * @return {Object} handle The custom handle data
21288      */
21289         getHandleFromEvent : function(e){
21290             var t = Roo.lib.Event.getTarget(e);
21291             return t ? handles[t.id] : null;
21292         },
21293
21294     /**
21295      * Returns a custom data object that is registered for a DOM node by id
21296      * @param {String|HTMLElement} id The DOM node or id to look up
21297      * @return {Object} data The custom data
21298      */
21299         getTarget : function(id){
21300             if(typeof id != "string"){ // must be element?
21301                 id = id.id;
21302             }
21303             return elements[id];
21304         },
21305
21306     /**
21307      * Returns a custom data object that is registered for the DOM node that is the target of the event
21308      * @param {Event} e The event
21309      * @return {Object} data The custom data
21310      */
21311         getTargetFromEvent : function(e){
21312             var t = Roo.lib.Event.getTarget(e);
21313             return t ? elements[t.id] || handles[t.id] : null;
21314         }
21315     };
21316 }();/*
21317  * Based on:
21318  * Ext JS Library 1.1.1
21319  * Copyright(c) 2006-2007, Ext JS, LLC.
21320  *
21321  * Originally Released Under LGPL - original licence link has changed is not relivant.
21322  *
21323  * Fork - LGPL
21324  * <script type="text/javascript">
21325  */
21326  
21327
21328 /**
21329  * @class Roo.dd.StatusProxy
21330  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21331  * default drag proxy used by all Roo.dd components.
21332  * @constructor
21333  * @param {Object} config
21334  */
21335 Roo.dd.StatusProxy = function(config){
21336     Roo.apply(this, config);
21337     this.id = this.id || Roo.id();
21338     this.el = new Roo.Layer({
21339         dh: {
21340             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21341                 {tag: "div", cls: "x-dd-drop-icon"},
21342                 {tag: "div", cls: "x-dd-drag-ghost"}
21343             ]
21344         }, 
21345         shadow: !config || config.shadow !== false
21346     });
21347     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21348     this.dropStatus = this.dropNotAllowed;
21349 };
21350
21351 Roo.dd.StatusProxy.prototype = {
21352     /**
21353      * @cfg {String} dropAllowed
21354      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
21355      */
21356     dropAllowed : "x-dd-drop-ok",
21357     /**
21358      * @cfg {String} dropNotAllowed
21359      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
21360      */
21361     dropNotAllowed : "x-dd-drop-nodrop",
21362
21363     /**
21364      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
21365      * over the current target element.
21366      * @param {String} cssClass The css class for the new drop status indicator image
21367      */
21368     setStatus : function(cssClass){
21369         cssClass = cssClass || this.dropNotAllowed;
21370         if(this.dropStatus != cssClass){
21371             this.el.replaceClass(this.dropStatus, cssClass);
21372             this.dropStatus = cssClass;
21373         }
21374     },
21375
21376     /**
21377      * Resets the status indicator to the default dropNotAllowed value
21378      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
21379      */
21380     reset : function(clearGhost){
21381         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
21382         this.dropStatus = this.dropNotAllowed;
21383         if(clearGhost){
21384             this.ghost.update("");
21385         }
21386     },
21387
21388     /**
21389      * Updates the contents of the ghost element
21390      * @param {String} html The html that will replace the current innerHTML of the ghost element
21391      */
21392     update : function(html){
21393         if(typeof html == "string"){
21394             this.ghost.update(html);
21395         }else{
21396             this.ghost.update("");
21397             html.style.margin = "0";
21398             this.ghost.dom.appendChild(html);
21399         }
21400         // ensure float = none set?? cant remember why though.
21401         var el = this.ghost.dom.firstChild;
21402                 if(el){
21403                         Roo.fly(el).setStyle('float', 'none');
21404                 }
21405     },
21406     
21407     /**
21408      * Returns the underlying proxy {@link Roo.Layer}
21409      * @return {Roo.Layer} el
21410     */
21411     getEl : function(){
21412         return this.el;
21413     },
21414
21415     /**
21416      * Returns the ghost element
21417      * @return {Roo.Element} el
21418      */
21419     getGhost : function(){
21420         return this.ghost;
21421     },
21422
21423     /**
21424      * Hides the proxy
21425      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
21426      */
21427     hide : function(clear){
21428         this.el.hide();
21429         if(clear){
21430             this.reset(true);
21431         }
21432     },
21433
21434     /**
21435      * Stops the repair animation if it's currently running
21436      */
21437     stop : function(){
21438         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
21439             this.anim.stop();
21440         }
21441     },
21442
21443     /**
21444      * Displays this proxy
21445      */
21446     show : function(){
21447         this.el.show();
21448     },
21449
21450     /**
21451      * Force the Layer to sync its shadow and shim positions to the element
21452      */
21453     sync : function(){
21454         this.el.sync();
21455     },
21456
21457     /**
21458      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
21459      * invalid drop operation by the item being dragged.
21460      * @param {Array} xy The XY position of the element ([x, y])
21461      * @param {Function} callback The function to call after the repair is complete
21462      * @param {Object} scope The scope in which to execute the callback
21463      */
21464     repair : function(xy, callback, scope){
21465         this.callback = callback;
21466         this.scope = scope;
21467         if(xy && this.animRepair !== false){
21468             this.el.addClass("x-dd-drag-repair");
21469             this.el.hideUnders(true);
21470             this.anim = this.el.shift({
21471                 duration: this.repairDuration || .5,
21472                 easing: 'easeOut',
21473                 xy: xy,
21474                 stopFx: true,
21475                 callback: this.afterRepair,
21476                 scope: this
21477             });
21478         }else{
21479             this.afterRepair();
21480         }
21481     },
21482
21483     // private
21484     afterRepair : function(){
21485         this.hide(true);
21486         if(typeof this.callback == "function"){
21487             this.callback.call(this.scope || this);
21488         }
21489         this.callback = null;
21490         this.scope = null;
21491     }
21492 };/*
21493  * Based on:
21494  * Ext JS Library 1.1.1
21495  * Copyright(c) 2006-2007, Ext JS, LLC.
21496  *
21497  * Originally Released Under LGPL - original licence link has changed is not relivant.
21498  *
21499  * Fork - LGPL
21500  * <script type="text/javascript">
21501  */
21502
21503 /**
21504  * @class Roo.dd.DragSource
21505  * @extends Roo.dd.DDProxy
21506  * A simple class that provides the basic implementation needed to make any element draggable.
21507  * @constructor
21508  * @param {String/HTMLElement/Element} el The container element
21509  * @param {Object} config
21510  */
21511 Roo.dd.DragSource = function(el, config){
21512     this.el = Roo.get(el);
21513     this.dragData = {};
21514     
21515     Roo.apply(this, config);
21516     
21517     if(!this.proxy){
21518         this.proxy = new Roo.dd.StatusProxy();
21519     }
21520
21521     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
21522           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
21523     
21524     this.dragging = false;
21525 };
21526
21527 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
21528     /**
21529      * @cfg {String} dropAllowed
21530      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21531      */
21532     dropAllowed : "x-dd-drop-ok",
21533     /**
21534      * @cfg {String} dropNotAllowed
21535      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21536      */
21537     dropNotAllowed : "x-dd-drop-nodrop",
21538
21539     /**
21540      * Returns the data object associated with this drag source
21541      * @return {Object} data An object containing arbitrary data
21542      */
21543     getDragData : function(e){
21544         return this.dragData;
21545     },
21546
21547     // private
21548     onDragEnter : function(e, id){
21549         var target = Roo.dd.DragDropMgr.getDDById(id);
21550         this.cachedTarget = target;
21551         if(this.beforeDragEnter(target, e, id) !== false){
21552             if(target.isNotifyTarget){
21553                 var status = target.notifyEnter(this, e, this.dragData);
21554                 this.proxy.setStatus(status);
21555             }else{
21556                 this.proxy.setStatus(this.dropAllowed);
21557             }
21558             
21559             if(this.afterDragEnter){
21560                 /**
21561                  * An empty function by default, but provided so that you can perform a custom action
21562                  * when the dragged item enters the drop target by providing an implementation.
21563                  * @param {Roo.dd.DragDrop} target The drop target
21564                  * @param {Event} e The event object
21565                  * @param {String} id The id of the dragged element
21566                  * @method afterDragEnter
21567                  */
21568                 this.afterDragEnter(target, e, id);
21569             }
21570         }
21571     },
21572
21573     /**
21574      * An empty function by default, but provided so that you can perform a custom action
21575      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
21576      * @param {Roo.dd.DragDrop} target The drop target
21577      * @param {Event} e The event object
21578      * @param {String} id The id of the dragged element
21579      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21580      */
21581     beforeDragEnter : function(target, e, id){
21582         return true;
21583     },
21584
21585     // private
21586     alignElWithMouse: function() {
21587         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
21588         this.proxy.sync();
21589     },
21590
21591     // private
21592     onDragOver : function(e, id){
21593         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21594         if(this.beforeDragOver(target, e, id) !== false){
21595             if(target.isNotifyTarget){
21596                 var status = target.notifyOver(this, e, this.dragData);
21597                 this.proxy.setStatus(status);
21598             }
21599
21600             if(this.afterDragOver){
21601                 /**
21602                  * An empty function by default, but provided so that you can perform a custom action
21603                  * while the dragged item is over the drop target by providing an implementation.
21604                  * @param {Roo.dd.DragDrop} target The drop target
21605                  * @param {Event} e The event object
21606                  * @param {String} id The id of the dragged element
21607                  * @method afterDragOver
21608                  */
21609                 this.afterDragOver(target, e, id);
21610             }
21611         }
21612     },
21613
21614     /**
21615      * An empty function by default, but provided so that you can perform a custom action
21616      * while the dragged item is over the drop target and optionally cancel the onDragOver.
21617      * @param {Roo.dd.DragDrop} target The drop target
21618      * @param {Event} e The event object
21619      * @param {String} id The id of the dragged element
21620      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21621      */
21622     beforeDragOver : function(target, e, id){
21623         return true;
21624     },
21625
21626     // private
21627     onDragOut : function(e, id){
21628         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21629         if(this.beforeDragOut(target, e, id) !== false){
21630             if(target.isNotifyTarget){
21631                 target.notifyOut(this, e, this.dragData);
21632             }
21633             this.proxy.reset();
21634             if(this.afterDragOut){
21635                 /**
21636                  * An empty function by default, but provided so that you can perform a custom action
21637                  * after the dragged item is dragged out of the target without dropping.
21638                  * @param {Roo.dd.DragDrop} target The drop target
21639                  * @param {Event} e The event object
21640                  * @param {String} id The id of the dragged element
21641                  * @method afterDragOut
21642                  */
21643                 this.afterDragOut(target, e, id);
21644             }
21645         }
21646         this.cachedTarget = null;
21647     },
21648
21649     /**
21650      * An empty function by default, but provided so that you can perform a custom action before the dragged
21651      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
21652      * @param {Roo.dd.DragDrop} target The drop target
21653      * @param {Event} e The event object
21654      * @param {String} id The id of the dragged element
21655      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21656      */
21657     beforeDragOut : function(target, e, id){
21658         return true;
21659     },
21660     
21661     // private
21662     onDragDrop : function(e, id){
21663         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21664         if(this.beforeDragDrop(target, e, id) !== false){
21665             if(target.isNotifyTarget){
21666                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
21667                     this.onValidDrop(target, e, id);
21668                 }else{
21669                     this.onInvalidDrop(target, e, id);
21670                 }
21671             }else{
21672                 this.onValidDrop(target, e, id);
21673             }
21674             
21675             if(this.afterDragDrop){
21676                 /**
21677                  * An empty function by default, but provided so that you can perform a custom action
21678                  * after a valid drag drop has occurred by providing an implementation.
21679                  * @param {Roo.dd.DragDrop} target The drop target
21680                  * @param {Event} e The event object
21681                  * @param {String} id The id of the dropped element
21682                  * @method afterDragDrop
21683                  */
21684                 this.afterDragDrop(target, e, id);
21685             }
21686         }
21687         delete this.cachedTarget;
21688     },
21689
21690     /**
21691      * An empty function by default, but provided so that you can perform a custom action before the dragged
21692      * item is dropped onto the target and optionally cancel the onDragDrop.
21693      * @param {Roo.dd.DragDrop} target The drop target
21694      * @param {Event} e The event object
21695      * @param {String} id The id of the dragged element
21696      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
21697      */
21698     beforeDragDrop : function(target, e, id){
21699         return true;
21700     },
21701
21702     // private
21703     onValidDrop : function(target, e, id){
21704         this.hideProxy();
21705         if(this.afterValidDrop){
21706             /**
21707              * An empty function by default, but provided so that you can perform a custom action
21708              * after a valid drop has occurred by providing an implementation.
21709              * @param {Object} target The target DD 
21710              * @param {Event} e The event object
21711              * @param {String} id The id of the dropped element
21712              * @method afterInvalidDrop
21713              */
21714             this.afterValidDrop(target, e, id);
21715         }
21716     },
21717
21718     // private
21719     getRepairXY : function(e, data){
21720         return this.el.getXY();  
21721     },
21722
21723     // private
21724     onInvalidDrop : function(target, e, id){
21725         this.beforeInvalidDrop(target, e, id);
21726         if(this.cachedTarget){
21727             if(this.cachedTarget.isNotifyTarget){
21728                 this.cachedTarget.notifyOut(this, e, this.dragData);
21729             }
21730             this.cacheTarget = null;
21731         }
21732         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
21733
21734         if(this.afterInvalidDrop){
21735             /**
21736              * An empty function by default, but provided so that you can perform a custom action
21737              * after an invalid drop has occurred by providing an implementation.
21738              * @param {Event} e The event object
21739              * @param {String} id The id of the dropped element
21740              * @method afterInvalidDrop
21741              */
21742             this.afterInvalidDrop(e, id);
21743         }
21744     },
21745
21746     // private
21747     afterRepair : function(){
21748         if(Roo.enableFx){
21749             this.el.highlight(this.hlColor || "c3daf9");
21750         }
21751         this.dragging = false;
21752     },
21753
21754     /**
21755      * An empty function by default, but provided so that you can perform a custom action after an invalid
21756      * drop has occurred.
21757      * @param {Roo.dd.DragDrop} target The drop target
21758      * @param {Event} e The event object
21759      * @param {String} id The id of the dragged element
21760      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
21761      */
21762     beforeInvalidDrop : function(target, e, id){
21763         return true;
21764     },
21765
21766     // private
21767     handleMouseDown : function(e){
21768         if(this.dragging) {
21769             return;
21770         }
21771         var data = this.getDragData(e);
21772         if(data && this.onBeforeDrag(data, e) !== false){
21773             this.dragData = data;
21774             this.proxy.stop();
21775             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
21776         } 
21777     },
21778
21779     /**
21780      * An empty function by default, but provided so that you can perform a custom action before the initial
21781      * drag event begins and optionally cancel it.
21782      * @param {Object} data An object containing arbitrary data to be shared with drop targets
21783      * @param {Event} e The event object
21784      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21785      */
21786     onBeforeDrag : function(data, e){
21787         return true;
21788     },
21789
21790     /**
21791      * An empty function by default, but provided so that you can perform a custom action once the initial
21792      * drag event has begun.  The drag cannot be canceled from this function.
21793      * @param {Number} x The x position of the click on the dragged object
21794      * @param {Number} y The y position of the click on the dragged object
21795      */
21796     onStartDrag : Roo.emptyFn,
21797
21798     // private - YUI override
21799     startDrag : function(x, y){
21800         this.proxy.reset();
21801         this.dragging = true;
21802         this.proxy.update("");
21803         this.onInitDrag(x, y);
21804         this.proxy.show();
21805     },
21806
21807     // private
21808     onInitDrag : function(x, y){
21809         var clone = this.el.dom.cloneNode(true);
21810         clone.id = Roo.id(); // prevent duplicate ids
21811         this.proxy.update(clone);
21812         this.onStartDrag(x, y);
21813         return true;
21814     },
21815
21816     /**
21817      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
21818      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
21819      */
21820     getProxy : function(){
21821         return this.proxy;  
21822     },
21823
21824     /**
21825      * Hides the drag source's {@link Roo.dd.StatusProxy}
21826      */
21827     hideProxy : function(){
21828         this.proxy.hide();  
21829         this.proxy.reset(true);
21830         this.dragging = false;
21831     },
21832
21833     // private
21834     triggerCacheRefresh : function(){
21835         Roo.dd.DDM.refreshCache(this.groups);
21836     },
21837
21838     // private - override to prevent hiding
21839     b4EndDrag: function(e) {
21840     },
21841
21842     // private - override to prevent moving
21843     endDrag : function(e){
21844         this.onEndDrag(this.dragData, e);
21845     },
21846
21847     // private
21848     onEndDrag : function(data, e){
21849     },
21850     
21851     // private - pin to cursor
21852     autoOffset : function(x, y) {
21853         this.setDelta(-12, -20);
21854     }    
21855 });/*
21856  * Based on:
21857  * Ext JS Library 1.1.1
21858  * Copyright(c) 2006-2007, Ext JS, LLC.
21859  *
21860  * Originally Released Under LGPL - original licence link has changed is not relivant.
21861  *
21862  * Fork - LGPL
21863  * <script type="text/javascript">
21864  */
21865
21866
21867 /**
21868  * @class Roo.dd.DropTarget
21869  * @extends Roo.dd.DDTarget
21870  * A simple class that provides the basic implementation needed to make any element a drop target that can have
21871  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
21872  * @constructor
21873  * @param {String/HTMLElement/Element} el The container element
21874  * @param {Object} config
21875  */
21876 Roo.dd.DropTarget = function(el, config){
21877     this.el = Roo.get(el);
21878     
21879     var listeners = false; ;
21880     if (config && config.listeners) {
21881         listeners= config.listeners;
21882         delete config.listeners;
21883     }
21884     Roo.apply(this, config);
21885     
21886     if(this.containerScroll){
21887         Roo.dd.ScrollManager.register(this.el);
21888     }
21889     this.addEvents( {
21890          /**
21891          * @scope Roo.dd.DropTarget
21892          */
21893          
21894          /**
21895          * @event enter
21896          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
21897          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
21898          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
21899          * 
21900          * IMPORTANT : it should set this.overClass and this.dropAllowed
21901          * 
21902          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21903          * @param {Event} e The event
21904          * @param {Object} data An object containing arbitrary data supplied by the drag source
21905          */
21906         "enter" : true,
21907         
21908          /**
21909          * @event over
21910          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
21911          * This method will be called on every mouse movement while the drag source is over the drop target.
21912          * This default implementation simply returns the dropAllowed config value.
21913          * 
21914          * IMPORTANT : it should set this.dropAllowed
21915          * 
21916          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21917          * @param {Event} e The event
21918          * @param {Object} data An object containing arbitrary data supplied by the drag source
21919          
21920          */
21921         "over" : true,
21922         /**
21923          * @event out
21924          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
21925          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
21926          * overClass (if any) from the drop element.
21927          * 
21928          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21929          * @param {Event} e The event
21930          * @param {Object} data An object containing arbitrary data supplied by the drag source
21931          */
21932          "out" : true,
21933          
21934         /**
21935          * @event drop
21936          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
21937          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
21938          * implementation that does something to process the drop event and returns true so that the drag source's
21939          * repair action does not run.
21940          * 
21941          * IMPORTANT : it should set this.success
21942          * 
21943          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21944          * @param {Event} e The event
21945          * @param {Object} data An object containing arbitrary data supplied by the drag source
21946         */
21947          "drop" : true
21948     });
21949             
21950      
21951     Roo.dd.DropTarget.superclass.constructor.call(  this, 
21952         this.el.dom, 
21953         this.ddGroup || this.group,
21954         {
21955             isTarget: true,
21956             listeners : listeners || {} 
21957            
21958         
21959         }
21960     );
21961
21962 };
21963
21964 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
21965     /**
21966      * @cfg {String} overClass
21967      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
21968      */
21969      /**
21970      * @cfg {String} ddGroup
21971      * The drag drop group to handle drop events for
21972      */
21973      
21974     /**
21975      * @cfg {String} dropAllowed
21976      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21977      */
21978     dropAllowed : "x-dd-drop-ok",
21979     /**
21980      * @cfg {String} dropNotAllowed
21981      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21982      */
21983     dropNotAllowed : "x-dd-drop-nodrop",
21984     /**
21985      * @cfg {boolean} success
21986      * set this after drop listener.. 
21987      */
21988     success : false,
21989     /**
21990      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
21991      * if the drop point is valid for over/enter..
21992      */
21993     valid : false,
21994     // private
21995     isTarget : true,
21996
21997     // private
21998     isNotifyTarget : true,
21999     
22000     /**
22001      * @hide
22002      */
22003     notifyEnter : function(dd, e, data)
22004     {
22005         this.valid = true;
22006         this.fireEvent('enter', dd, e, data);
22007         if(this.overClass){
22008             this.el.addClass(this.overClass);
22009         }
22010         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22011             this.valid ? this.dropAllowed : this.dropNotAllowed
22012         );
22013     },
22014
22015     /**
22016      * @hide
22017      */
22018     notifyOver : function(dd, e, data)
22019     {
22020         this.valid = true;
22021         this.fireEvent('over', dd, e, data);
22022         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22023             this.valid ? this.dropAllowed : this.dropNotAllowed
22024         );
22025     },
22026
22027     /**
22028      * @hide
22029      */
22030     notifyOut : function(dd, e, data)
22031     {
22032         this.fireEvent('out', dd, e, data);
22033         if(this.overClass){
22034             this.el.removeClass(this.overClass);
22035         }
22036     },
22037
22038     /**
22039      * @hide
22040      */
22041     notifyDrop : function(dd, e, data)
22042     {
22043         this.success = false;
22044         this.fireEvent('drop', dd, e, data);
22045         return this.success;
22046     }
22047 });/*
22048  * Based on:
22049  * Ext JS Library 1.1.1
22050  * Copyright(c) 2006-2007, Ext JS, LLC.
22051  *
22052  * Originally Released Under LGPL - original licence link has changed is not relivant.
22053  *
22054  * Fork - LGPL
22055  * <script type="text/javascript">
22056  */
22057
22058
22059 /**
22060  * @class Roo.dd.DragZone
22061  * @extends Roo.dd.DragSource
22062  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22063  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22064  * @constructor
22065  * @param {String/HTMLElement/Element} el The container element
22066  * @param {Object} config
22067  */
22068 Roo.dd.DragZone = function(el, config){
22069     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22070     if(this.containerScroll){
22071         Roo.dd.ScrollManager.register(this.el);
22072     }
22073 };
22074
22075 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22076     /**
22077      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22078      * for auto scrolling during drag operations.
22079      */
22080     /**
22081      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22082      * method after a failed drop (defaults to "c3daf9" - light blue)
22083      */
22084
22085     /**
22086      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22087      * for a valid target to drag based on the mouse down. Override this method
22088      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22089      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22090      * @param {EventObject} e The mouse down event
22091      * @return {Object} The dragData
22092      */
22093     getDragData : function(e){
22094         return Roo.dd.Registry.getHandleFromEvent(e);
22095     },
22096     
22097     /**
22098      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22099      * this.dragData.ddel
22100      * @param {Number} x The x position of the click on the dragged object
22101      * @param {Number} y The y position of the click on the dragged object
22102      * @return {Boolean} true to continue the drag, false to cancel
22103      */
22104     onInitDrag : function(x, y){
22105         this.proxy.update(this.dragData.ddel.cloneNode(true));
22106         this.onStartDrag(x, y);
22107         return true;
22108     },
22109     
22110     /**
22111      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22112      */
22113     afterRepair : function(){
22114         if(Roo.enableFx){
22115             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22116         }
22117         this.dragging = false;
22118     },
22119
22120     /**
22121      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22122      * the XY of this.dragData.ddel
22123      * @param {EventObject} e The mouse up event
22124      * @return {Array} The xy location (e.g. [100, 200])
22125      */
22126     getRepairXY : function(e){
22127         return Roo.Element.fly(this.dragData.ddel).getXY();  
22128     }
22129 });/*
22130  * Based on:
22131  * Ext JS Library 1.1.1
22132  * Copyright(c) 2006-2007, Ext JS, LLC.
22133  *
22134  * Originally Released Under LGPL - original licence link has changed is not relivant.
22135  *
22136  * Fork - LGPL
22137  * <script type="text/javascript">
22138  */
22139 /**
22140  * @class Roo.dd.DropZone
22141  * @extends Roo.dd.DropTarget
22142  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22143  * By default, this class requires that child nodes accepting drop 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.DropZone = function(el, config){
22149     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22150 };
22151
22152 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22153     /**
22154      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22155      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22156      * provide your own custom lookup.
22157      * @param {Event} e The event
22158      * @return {Object} data The custom data
22159      */
22160     getTargetFromEvent : function(e){
22161         return Roo.dd.Registry.getTargetFromEvent(e);
22162     },
22163
22164     /**
22165      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22166      * that it has registered.  This method has no default implementation and should be overridden to provide
22167      * node-specific processing if necessary.
22168      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22169      * {@link #getTargetFromEvent} for this node)
22170      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22171      * @param {Event} e The event
22172      * @param {Object} data An object containing arbitrary data supplied by the drag source
22173      */
22174     onNodeEnter : function(n, dd, e, data){
22175         
22176     },
22177
22178     /**
22179      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22180      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22181      * overridden to provide the proper feedback.
22182      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22183      * {@link #getTargetFromEvent} for this node)
22184      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22185      * @param {Event} e The event
22186      * @param {Object} data An object containing arbitrary data supplied by the drag source
22187      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22188      * underlying {@link Roo.dd.StatusProxy} can be updated
22189      */
22190     onNodeOver : function(n, dd, e, data){
22191         return this.dropAllowed;
22192     },
22193
22194     /**
22195      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22196      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22197      * node-specific processing if necessary.
22198      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22199      * {@link #getTargetFromEvent} for this node)
22200      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22201      * @param {Event} e The event
22202      * @param {Object} data An object containing arbitrary data supplied by the drag source
22203      */
22204     onNodeOut : function(n, dd, e, data){
22205         
22206     },
22207
22208     /**
22209      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22210      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22211      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22212      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22213      * {@link #getTargetFromEvent} for this node)
22214      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22215      * @param {Event} e The event
22216      * @param {Object} data An object containing arbitrary data supplied by the drag source
22217      * @return {Boolean} True if the drop was valid, else false
22218      */
22219     onNodeDrop : function(n, dd, e, data){
22220         return false;
22221     },
22222
22223     /**
22224      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22225      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22226      * it should be overridden to provide the proper feedback if necessary.
22227      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22228      * @param {Event} e The event
22229      * @param {Object} data An object containing arbitrary data supplied by the drag source
22230      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22231      * underlying {@link Roo.dd.StatusProxy} can be updated
22232      */
22233     onContainerOver : function(dd, e, data){
22234         return this.dropNotAllowed;
22235     },
22236
22237     /**
22238      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22239      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22240      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22241      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22242      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22243      * @param {Event} e The event
22244      * @param {Object} data An object containing arbitrary data supplied by the drag source
22245      * @return {Boolean} True if the drop was valid, else false
22246      */
22247     onContainerDrop : function(dd, e, data){
22248         return false;
22249     },
22250
22251     /**
22252      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22253      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22254      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22255      * you should override this method and provide a custom implementation.
22256      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22257      * @param {Event} e The event
22258      * @param {Object} data An object containing arbitrary data supplied by the drag source
22259      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22260      * underlying {@link Roo.dd.StatusProxy} can be updated
22261      */
22262     notifyEnter : function(dd, e, data){
22263         return this.dropNotAllowed;
22264     },
22265
22266     /**
22267      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22268      * This method will be called on every mouse movement while the drag source is over the drop zone.
22269      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22270      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22271      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22272      * registered node, it will call {@link #onContainerOver}.
22273      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22274      * @param {Event} e The event
22275      * @param {Object} data An object containing arbitrary data supplied by the drag source
22276      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22277      * underlying {@link Roo.dd.StatusProxy} can be updated
22278      */
22279     notifyOver : function(dd, e, data){
22280         var n = this.getTargetFromEvent(e);
22281         if(!n){ // not over valid drop target
22282             if(this.lastOverNode){
22283                 this.onNodeOut(this.lastOverNode, dd, e, data);
22284                 this.lastOverNode = null;
22285             }
22286             return this.onContainerOver(dd, e, data);
22287         }
22288         if(this.lastOverNode != n){
22289             if(this.lastOverNode){
22290                 this.onNodeOut(this.lastOverNode, dd, e, data);
22291             }
22292             this.onNodeEnter(n, dd, e, data);
22293             this.lastOverNode = n;
22294         }
22295         return this.onNodeOver(n, dd, e, data);
22296     },
22297
22298     /**
22299      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22300      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22301      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22302      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22303      * @param {Event} e The event
22304      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22305      */
22306     notifyOut : function(dd, e, data){
22307         if(this.lastOverNode){
22308             this.onNodeOut(this.lastOverNode, dd, e, data);
22309             this.lastOverNode = null;
22310         }
22311     },
22312
22313     /**
22314      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22315      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22316      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22317      * otherwise it will call {@link #onContainerDrop}.
22318      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22319      * @param {Event} e The event
22320      * @param {Object} data An object containing arbitrary data supplied by the drag source
22321      * @return {Boolean} True if the drop was valid, else false
22322      */
22323     notifyDrop : function(dd, e, data){
22324         if(this.lastOverNode){
22325             this.onNodeOut(this.lastOverNode, dd, e, data);
22326             this.lastOverNode = null;
22327         }
22328         var n = this.getTargetFromEvent(e);
22329         return n ?
22330             this.onNodeDrop(n, dd, e, data) :
22331             this.onContainerDrop(dd, e, data);
22332     },
22333
22334     // private
22335     triggerCacheRefresh : function(){
22336         Roo.dd.DDM.refreshCache(this.groups);
22337     }  
22338 });/*
22339  * Based on:
22340  * Ext JS Library 1.1.1
22341  * Copyright(c) 2006-2007, Ext JS, LLC.
22342  *
22343  * Originally Released Under LGPL - original licence link has changed is not relivant.
22344  *
22345  * Fork - LGPL
22346  * <script type="text/javascript">
22347  */
22348
22349
22350 /**
22351  * @class Roo.data.SortTypes
22352  * @singleton
22353  * Defines the default sorting (casting?) comparison functions used when sorting data.
22354  */
22355 Roo.data.SortTypes = {
22356     /**
22357      * Default sort that does nothing
22358      * @param {Mixed} s The value being converted
22359      * @return {Mixed} The comparison value
22360      */
22361     none : function(s){
22362         return s;
22363     },
22364     
22365     /**
22366      * The regular expression used to strip tags
22367      * @type {RegExp}
22368      * @property
22369      */
22370     stripTagsRE : /<\/?[^>]+>/gi,
22371     
22372     /**
22373      * Strips all HTML tags to sort on text only
22374      * @param {Mixed} s The value being converted
22375      * @return {String} The comparison value
22376      */
22377     asText : function(s){
22378         return String(s).replace(this.stripTagsRE, "");
22379     },
22380     
22381     /**
22382      * Strips all HTML tags to sort on text only - Case insensitive
22383      * @param {Mixed} s The value being converted
22384      * @return {String} The comparison value
22385      */
22386     asUCText : function(s){
22387         return String(s).toUpperCase().replace(this.stripTagsRE, "");
22388     },
22389     
22390     /**
22391      * Case insensitive string
22392      * @param {Mixed} s The value being converted
22393      * @return {String} The comparison value
22394      */
22395     asUCString : function(s) {
22396         return String(s).toUpperCase();
22397     },
22398     
22399     /**
22400      * Date sorting
22401      * @param {Mixed} s The value being converted
22402      * @return {Number} The comparison value
22403      */
22404     asDate : function(s) {
22405         if(!s){
22406             return 0;
22407         }
22408         if(s instanceof Date){
22409             return s.getTime();
22410         }
22411         return Date.parse(String(s));
22412     },
22413     
22414     /**
22415      * Float sorting
22416      * @param {Mixed} s The value being converted
22417      * @return {Float} The comparison value
22418      */
22419     asFloat : function(s) {
22420         var val = parseFloat(String(s).replace(/,/g, ""));
22421         if(isNaN(val)) {
22422             val = 0;
22423         }
22424         return val;
22425     },
22426     
22427     /**
22428      * Integer sorting
22429      * @param {Mixed} s The value being converted
22430      * @return {Number} The comparison value
22431      */
22432     asInt : function(s) {
22433         var val = parseInt(String(s).replace(/,/g, ""));
22434         if(isNaN(val)) {
22435             val = 0;
22436         }
22437         return val;
22438     }
22439 };/*
22440  * Based on:
22441  * Ext JS Library 1.1.1
22442  * Copyright(c) 2006-2007, Ext JS, LLC.
22443  *
22444  * Originally Released Under LGPL - original licence link has changed is not relivant.
22445  *
22446  * Fork - LGPL
22447  * <script type="text/javascript">
22448  */
22449
22450 /**
22451 * @class Roo.data.Record
22452  * Instances of this class encapsulate both record <em>definition</em> information, and record
22453  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
22454  * to access Records cached in an {@link Roo.data.Store} object.<br>
22455  * <p>
22456  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
22457  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
22458  * objects.<br>
22459  * <p>
22460  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
22461  * @constructor
22462  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
22463  * {@link #create}. The parameters are the same.
22464  * @param {Array} data An associative Array of data values keyed by the field name.
22465  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
22466  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
22467  * not specified an integer id is generated.
22468  */
22469 Roo.data.Record = function(data, id){
22470     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
22471     this.data = data;
22472 };
22473
22474 /**
22475  * Generate a constructor for a specific record layout.
22476  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
22477  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
22478  * Each field definition object may contain the following properties: <ul>
22479  * <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,
22480  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
22481  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
22482  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
22483  * is being used, then this is a string containing the javascript expression to reference the data relative to 
22484  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
22485  * to the data item relative to the record element. If the mapping expression is the same as the field name,
22486  * this may be omitted.</p></li>
22487  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
22488  * <ul><li>auto (Default, implies no conversion)</li>
22489  * <li>string</li>
22490  * <li>int</li>
22491  * <li>float</li>
22492  * <li>boolean</li>
22493  * <li>date</li></ul></p></li>
22494  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
22495  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
22496  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
22497  * by the Reader into an object that will be stored in the Record. It is passed the
22498  * following parameters:<ul>
22499  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
22500  * </ul></p></li>
22501  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
22502  * </ul>
22503  * <br>usage:<br><pre><code>
22504 var TopicRecord = Roo.data.Record.create(
22505     {name: 'title', mapping: 'topic_title'},
22506     {name: 'author', mapping: 'username'},
22507     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
22508     {name: 'lastPost', mapping: 'post_time', type: 'date'},
22509     {name: 'lastPoster', mapping: 'user2'},
22510     {name: 'excerpt', mapping: 'post_text'}
22511 );
22512
22513 var myNewRecord = new TopicRecord({
22514     title: 'Do my job please',
22515     author: 'noobie',
22516     totalPosts: 1,
22517     lastPost: new Date(),
22518     lastPoster: 'Animal',
22519     excerpt: 'No way dude!'
22520 });
22521 myStore.add(myNewRecord);
22522 </code></pre>
22523  * @method create
22524  * @static
22525  */
22526 Roo.data.Record.create = function(o){
22527     var f = function(){
22528         f.superclass.constructor.apply(this, arguments);
22529     };
22530     Roo.extend(f, Roo.data.Record);
22531     var p = f.prototype;
22532     p.fields = new Roo.util.MixedCollection(false, function(field){
22533         return field.name;
22534     });
22535     for(var i = 0, len = o.length; i < len; i++){
22536         p.fields.add(new Roo.data.Field(o[i]));
22537     }
22538     f.getField = function(name){
22539         return p.fields.get(name);  
22540     };
22541     return f;
22542 };
22543
22544 Roo.data.Record.AUTO_ID = 1000;
22545 Roo.data.Record.EDIT = 'edit';
22546 Roo.data.Record.REJECT = 'reject';
22547 Roo.data.Record.COMMIT = 'commit';
22548
22549 Roo.data.Record.prototype = {
22550     /**
22551      * Readonly flag - true if this record has been modified.
22552      * @type Boolean
22553      */
22554     dirty : false,
22555     editing : false,
22556     error: null,
22557     modified: null,
22558
22559     // private
22560     join : function(store){
22561         this.store = store;
22562     },
22563
22564     /**
22565      * Set the named field to the specified value.
22566      * @param {String} name The name of the field to set.
22567      * @param {Object} value The value to set the field to.
22568      */
22569     set : function(name, value){
22570         if(this.data[name] == value){
22571             return;
22572         }
22573         this.dirty = true;
22574         if(!this.modified){
22575             this.modified = {};
22576         }
22577         if(typeof this.modified[name] == 'undefined'){
22578             this.modified[name] = this.data[name];
22579         }
22580         this.data[name] = value;
22581         if(!this.editing && this.store){
22582             this.store.afterEdit(this);
22583         }       
22584     },
22585
22586     /**
22587      * Get the value of the named field.
22588      * @param {String} name The name of the field to get the value of.
22589      * @return {Object} The value of the field.
22590      */
22591     get : function(name){
22592         return this.data[name]; 
22593     },
22594
22595     // private
22596     beginEdit : function(){
22597         this.editing = true;
22598         this.modified = {}; 
22599     },
22600
22601     // private
22602     cancelEdit : function(){
22603         this.editing = false;
22604         delete this.modified;
22605     },
22606
22607     // private
22608     endEdit : function(){
22609         this.editing = false;
22610         if(this.dirty && this.store){
22611             this.store.afterEdit(this);
22612         }
22613     },
22614
22615     /**
22616      * Usually called by the {@link Roo.data.Store} which owns the Record.
22617      * Rejects all changes made to the Record since either creation, or the last commit operation.
22618      * Modified fields are reverted to their original values.
22619      * <p>
22620      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22621      * of reject operations.
22622      */
22623     reject : function(){
22624         var m = this.modified;
22625         for(var n in m){
22626             if(typeof m[n] != "function"){
22627                 this.data[n] = m[n];
22628             }
22629         }
22630         this.dirty = false;
22631         delete this.modified;
22632         this.editing = false;
22633         if(this.store){
22634             this.store.afterReject(this);
22635         }
22636     },
22637
22638     /**
22639      * Usually called by the {@link Roo.data.Store} which owns the Record.
22640      * Commits all changes made to the Record since either creation, or the last commit operation.
22641      * <p>
22642      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22643      * of commit operations.
22644      */
22645     commit : function(){
22646         this.dirty = false;
22647         delete this.modified;
22648         this.editing = false;
22649         if(this.store){
22650             this.store.afterCommit(this);
22651         }
22652     },
22653
22654     // private
22655     hasError : function(){
22656         return this.error != null;
22657     },
22658
22659     // private
22660     clearError : function(){
22661         this.error = null;
22662     },
22663
22664     /**
22665      * Creates a copy of this record.
22666      * @param {String} id (optional) A new record id if you don't want to use this record's id
22667      * @return {Record}
22668      */
22669     copy : function(newId) {
22670         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
22671     }
22672 };/*
22673  * Based on:
22674  * Ext JS Library 1.1.1
22675  * Copyright(c) 2006-2007, Ext JS, LLC.
22676  *
22677  * Originally Released Under LGPL - original licence link has changed is not relivant.
22678  *
22679  * Fork - LGPL
22680  * <script type="text/javascript">
22681  */
22682
22683
22684
22685 /**
22686  * @class Roo.data.Store
22687  * @extends Roo.util.Observable
22688  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
22689  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
22690  * <p>
22691  * 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
22692  * has no knowledge of the format of the data returned by the Proxy.<br>
22693  * <p>
22694  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
22695  * instances from the data object. These records are cached and made available through accessor functions.
22696  * @constructor
22697  * Creates a new Store.
22698  * @param {Object} config A config object containing the objects needed for the Store to access data,
22699  * and read the data into Records.
22700  */
22701 Roo.data.Store = function(config){
22702     this.data = new Roo.util.MixedCollection(false);
22703     this.data.getKey = function(o){
22704         return o.id;
22705     };
22706     this.baseParams = {};
22707     // private
22708     this.paramNames = {
22709         "start" : "start",
22710         "limit" : "limit",
22711         "sort" : "sort",
22712         "dir" : "dir",
22713         "multisort" : "_multisort"
22714     };
22715
22716     if(config && config.data){
22717         this.inlineData = config.data;
22718         delete config.data;
22719     }
22720
22721     Roo.apply(this, config);
22722     
22723     if(this.reader){ // reader passed
22724         this.reader = Roo.factory(this.reader, Roo.data);
22725         this.reader.xmodule = this.xmodule || false;
22726         if(!this.recordType){
22727             this.recordType = this.reader.recordType;
22728         }
22729         if(this.reader.onMetaChange){
22730             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
22731         }
22732     }
22733
22734     if(this.recordType){
22735         this.fields = this.recordType.prototype.fields;
22736     }
22737     this.modified = [];
22738
22739     this.addEvents({
22740         /**
22741          * @event datachanged
22742          * Fires when the data cache has changed, and a widget which is using this Store
22743          * as a Record cache should refresh its view.
22744          * @param {Store} this
22745          */
22746         datachanged : true,
22747         /**
22748          * @event metachange
22749          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
22750          * @param {Store} this
22751          * @param {Object} meta The JSON metadata
22752          */
22753         metachange : true,
22754         /**
22755          * @event add
22756          * Fires when Records have been added to the Store
22757          * @param {Store} this
22758          * @param {Roo.data.Record[]} records The array of Records added
22759          * @param {Number} index The index at which the record(s) were added
22760          */
22761         add : true,
22762         /**
22763          * @event remove
22764          * Fires when a Record has been removed from the Store
22765          * @param {Store} this
22766          * @param {Roo.data.Record} record The Record that was removed
22767          * @param {Number} index The index at which the record was removed
22768          */
22769         remove : true,
22770         /**
22771          * @event update
22772          * Fires when a Record has been updated
22773          * @param {Store} this
22774          * @param {Roo.data.Record} record The Record that was updated
22775          * @param {String} operation The update operation being performed.  Value may be one of:
22776          * <pre><code>
22777  Roo.data.Record.EDIT
22778  Roo.data.Record.REJECT
22779  Roo.data.Record.COMMIT
22780          * </code></pre>
22781          */
22782         update : true,
22783         /**
22784          * @event clear
22785          * Fires when the data cache has been cleared.
22786          * @param {Store} this
22787          */
22788         clear : true,
22789         /**
22790          * @event beforeload
22791          * Fires before a request is made for a new data object.  If the beforeload handler returns false
22792          * the load action will be canceled.
22793          * @param {Store} this
22794          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22795          */
22796         beforeload : true,
22797         /**
22798          * @event beforeloadadd
22799          * Fires after a new set of Records has been loaded.
22800          * @param {Store} this
22801          * @param {Roo.data.Record[]} records The Records that were loaded
22802          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22803          */
22804         beforeloadadd : true,
22805         /**
22806          * @event load
22807          * Fires after a new set of Records has been loaded, before they are added to the store.
22808          * @param {Store} this
22809          * @param {Roo.data.Record[]} records The Records that were loaded
22810          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22811          * @params {Object} return from reader
22812          */
22813         load : true,
22814         /**
22815          * @event loadexception
22816          * Fires if an exception occurs in the Proxy during loading.
22817          * Called with the signature of the Proxy's "loadexception" event.
22818          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
22819          * 
22820          * @param {Proxy} 
22821          * @param {Object} return from JsonData.reader() - success, totalRecords, records
22822          * @param {Object} load options 
22823          * @param {Object} jsonData from your request (normally this contains the Exception)
22824          */
22825         loadexception : true
22826     });
22827     
22828     if(this.proxy){
22829         this.proxy = Roo.factory(this.proxy, Roo.data);
22830         this.proxy.xmodule = this.xmodule || false;
22831         this.relayEvents(this.proxy,  ["loadexception"]);
22832     }
22833     this.sortToggle = {};
22834     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
22835
22836     Roo.data.Store.superclass.constructor.call(this);
22837
22838     if(this.inlineData){
22839         this.loadData(this.inlineData);
22840         delete this.inlineData;
22841     }
22842 };
22843
22844 Roo.extend(Roo.data.Store, Roo.util.Observable, {
22845      /**
22846     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
22847     * without a remote query - used by combo/forms at present.
22848     */
22849     
22850     /**
22851     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
22852     */
22853     /**
22854     * @cfg {Array} data Inline data to be loaded when the store is initialized.
22855     */
22856     /**
22857     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
22858     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
22859     */
22860     /**
22861     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
22862     * on any HTTP request
22863     */
22864     /**
22865     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
22866     */
22867     /**
22868     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
22869     */
22870     multiSort: false,
22871     /**
22872     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
22873     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
22874     */
22875     remoteSort : false,
22876
22877     /**
22878     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
22879      * loaded or when a record is removed. (defaults to false).
22880     */
22881     pruneModifiedRecords : false,
22882
22883     // private
22884     lastOptions : null,
22885
22886     /**
22887      * Add Records to the Store and fires the add event.
22888      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22889      */
22890     add : function(records){
22891         records = [].concat(records);
22892         for(var i = 0, len = records.length; i < len; i++){
22893             records[i].join(this);
22894         }
22895         var index = this.data.length;
22896         this.data.addAll(records);
22897         this.fireEvent("add", this, records, index);
22898     },
22899
22900     /**
22901      * Remove a Record from the Store and fires the remove event.
22902      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
22903      */
22904     remove : function(record){
22905         var index = this.data.indexOf(record);
22906         this.data.removeAt(index);
22907         if(this.pruneModifiedRecords){
22908             this.modified.remove(record);
22909         }
22910         this.fireEvent("remove", this, record, index);
22911     },
22912
22913     /**
22914      * Remove all Records from the Store and fires the clear event.
22915      */
22916     removeAll : function(){
22917         this.data.clear();
22918         if(this.pruneModifiedRecords){
22919             this.modified = [];
22920         }
22921         this.fireEvent("clear", this);
22922     },
22923
22924     /**
22925      * Inserts Records to the Store at the given index and fires the add event.
22926      * @param {Number} index The start index at which to insert the passed Records.
22927      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22928      */
22929     insert : function(index, records){
22930         records = [].concat(records);
22931         for(var i = 0, len = records.length; i < len; i++){
22932             this.data.insert(index, records[i]);
22933             records[i].join(this);
22934         }
22935         this.fireEvent("add", this, records, index);
22936     },
22937
22938     /**
22939      * Get the index within the cache of the passed Record.
22940      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
22941      * @return {Number} The index of the passed Record. Returns -1 if not found.
22942      */
22943     indexOf : function(record){
22944         return this.data.indexOf(record);
22945     },
22946
22947     /**
22948      * Get the index within the cache of the Record with the passed id.
22949      * @param {String} id The id of the Record to find.
22950      * @return {Number} The index of the Record. Returns -1 if not found.
22951      */
22952     indexOfId : function(id){
22953         return this.data.indexOfKey(id);
22954     },
22955
22956     /**
22957      * Get the Record with the specified id.
22958      * @param {String} id The id of the Record to find.
22959      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
22960      */
22961     getById : function(id){
22962         return this.data.key(id);
22963     },
22964
22965     /**
22966      * Get the Record at the specified index.
22967      * @param {Number} index The index of the Record to find.
22968      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
22969      */
22970     getAt : function(index){
22971         return this.data.itemAt(index);
22972     },
22973
22974     /**
22975      * Returns a range of Records between specified indices.
22976      * @param {Number} startIndex (optional) The starting index (defaults to 0)
22977      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
22978      * @return {Roo.data.Record[]} An array of Records
22979      */
22980     getRange : function(start, end){
22981         return this.data.getRange(start, end);
22982     },
22983
22984     // private
22985     storeOptions : function(o){
22986         o = Roo.apply({}, o);
22987         delete o.callback;
22988         delete o.scope;
22989         this.lastOptions = o;
22990     },
22991
22992     /**
22993      * Loads the Record cache from the configured Proxy using the configured Reader.
22994      * <p>
22995      * If using remote paging, then the first load call must specify the <em>start</em>
22996      * and <em>limit</em> properties in the options.params property to establish the initial
22997      * position within the dataset, and the number of Records to cache on each read from the Proxy.
22998      * <p>
22999      * <strong>It is important to note that for remote data sources, loading is asynchronous,
23000      * and this call will return before the new data has been loaded. Perform any post-processing
23001      * in a callback function, or in a "load" event handler.</strong>
23002      * <p>
23003      * @param {Object} options An object containing properties which control loading options:<ul>
23004      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23005      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23006      * passed the following arguments:<ul>
23007      * <li>r : Roo.data.Record[]</li>
23008      * <li>options: Options object from the load call</li>
23009      * <li>success: Boolean success indicator</li></ul></li>
23010      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23011      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23012      * </ul>
23013      */
23014     load : function(options){
23015         options = options || {};
23016         if(this.fireEvent("beforeload", this, options) !== false){
23017             this.storeOptions(options);
23018             var p = Roo.apply(options.params || {}, this.baseParams);
23019             // if meta was not loaded from remote source.. try requesting it.
23020             if (!this.reader.metaFromRemote) {
23021                 p._requestMeta = 1;
23022             }
23023             if(this.sortInfo && this.remoteSort){
23024                 var pn = this.paramNames;
23025                 p[pn["sort"]] = this.sortInfo.field;
23026                 p[pn["dir"]] = this.sortInfo.direction;
23027             }
23028             if (this.multiSort) {
23029                 var pn = this.paramNames;
23030                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23031             }
23032             
23033             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23034         }
23035     },
23036
23037     /**
23038      * Reloads the Record cache from the configured Proxy using the configured Reader and
23039      * the options from the last load operation performed.
23040      * @param {Object} options (optional) An object containing properties which may override the options
23041      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23042      * the most recently used options are reused).
23043      */
23044     reload : function(options){
23045         this.load(Roo.applyIf(options||{}, this.lastOptions));
23046     },
23047
23048     // private
23049     // Called as a callback by the Reader during a load operation.
23050     loadRecords : function(o, options, success){
23051         if(!o || success === false){
23052             if(success !== false){
23053                 this.fireEvent("load", this, [], options, o);
23054             }
23055             if(options.callback){
23056                 options.callback.call(options.scope || this, [], options, false);
23057             }
23058             return;
23059         }
23060         // if data returned failure - throw an exception.
23061         if (o.success === false) {
23062             // show a message if no listener is registered.
23063             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23064                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23065             }
23066             // loadmask wil be hooked into this..
23067             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23068             return;
23069         }
23070         var r = o.records, t = o.totalRecords || r.length;
23071         
23072         this.fireEvent("beforeloadadd", this, r, options, o);
23073         
23074         if(!options || options.add !== true){
23075             if(this.pruneModifiedRecords){
23076                 this.modified = [];
23077             }
23078             for(var i = 0, len = r.length; i < len; i++){
23079                 r[i].join(this);
23080             }
23081             if(this.snapshot){
23082                 this.data = this.snapshot;
23083                 delete this.snapshot;
23084             }
23085             this.data.clear();
23086             this.data.addAll(r);
23087             this.totalLength = t;
23088             this.applySort();
23089             this.fireEvent("datachanged", this);
23090         }else{
23091             this.totalLength = Math.max(t, this.data.length+r.length);
23092             this.add(r);
23093         }
23094         
23095         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23096                 
23097             var e = new Roo.data.Record({});
23098
23099             e.set(this.parent.displayField, this.parent.emptyTitle);
23100             e.set(this.parent.valueField, '');
23101
23102             this.insert(0, e);
23103         }
23104             
23105         this.fireEvent("load", this, r, options, o);
23106         if(options.callback){
23107             options.callback.call(options.scope || this, r, options, true);
23108         }
23109     },
23110
23111
23112     /**
23113      * Loads data from a passed data block. A Reader which understands the format of the data
23114      * must have been configured in the constructor.
23115      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23116      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23117      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23118      */
23119     loadData : function(o, append){
23120         var r = this.reader.readRecords(o);
23121         this.loadRecords(r, {add: append}, true);
23122     },
23123
23124     /**
23125      * Gets the number of cached records.
23126      * <p>
23127      * <em>If using paging, this may not be the total size of the dataset. If the data object
23128      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23129      * the data set size</em>
23130      */
23131     getCount : function(){
23132         return this.data.length || 0;
23133     },
23134
23135     /**
23136      * Gets the total number of records in the dataset as returned by the server.
23137      * <p>
23138      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23139      * the dataset size</em>
23140      */
23141     getTotalCount : function(){
23142         return this.totalLength || 0;
23143     },
23144
23145     /**
23146      * Returns the sort state of the Store as an object with two properties:
23147      * <pre><code>
23148  field {String} The name of the field by which the Records are sorted
23149  direction {String} The sort order, "ASC" or "DESC"
23150      * </code></pre>
23151      */
23152     getSortState : function(){
23153         return this.sortInfo;
23154     },
23155
23156     // private
23157     applySort : function(){
23158         if(this.sortInfo && !this.remoteSort){
23159             var s = this.sortInfo, f = s.field;
23160             var st = this.fields.get(f).sortType;
23161             var fn = function(r1, r2){
23162                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23163                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23164             };
23165             this.data.sort(s.direction, fn);
23166             if(this.snapshot && this.snapshot != this.data){
23167                 this.snapshot.sort(s.direction, fn);
23168             }
23169         }
23170     },
23171
23172     /**
23173      * Sets the default sort column and order to be used by the next load operation.
23174      * @param {String} fieldName The name of the field to sort by.
23175      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23176      */
23177     setDefaultSort : function(field, dir){
23178         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23179     },
23180
23181     /**
23182      * Sort the Records.
23183      * If remote sorting is used, the sort is performed on the server, and the cache is
23184      * reloaded. If local sorting is used, the cache is sorted internally.
23185      * @param {String} fieldName The name of the field to sort by.
23186      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23187      */
23188     sort : function(fieldName, dir){
23189         var f = this.fields.get(fieldName);
23190         if(!dir){
23191             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23192             
23193             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23194                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23195             }else{
23196                 dir = f.sortDir;
23197             }
23198         }
23199         this.sortToggle[f.name] = dir;
23200         this.sortInfo = {field: f.name, direction: dir};
23201         if(!this.remoteSort){
23202             this.applySort();
23203             this.fireEvent("datachanged", this);
23204         }else{
23205             this.load(this.lastOptions);
23206         }
23207     },
23208
23209     /**
23210      * Calls the specified function for each of the Records in the cache.
23211      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23212      * Returning <em>false</em> aborts and exits the iteration.
23213      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23214      */
23215     each : function(fn, scope){
23216         this.data.each(fn, scope);
23217     },
23218
23219     /**
23220      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23221      * (e.g., during paging).
23222      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23223      */
23224     getModifiedRecords : function(){
23225         return this.modified;
23226     },
23227
23228     // private
23229     createFilterFn : function(property, value, anyMatch){
23230         if(!value.exec){ // not a regex
23231             value = String(value);
23232             if(value.length == 0){
23233                 return false;
23234             }
23235             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23236         }
23237         return function(r){
23238             return value.test(r.data[property]);
23239         };
23240     },
23241
23242     /**
23243      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23244      * @param {String} property A field on your records
23245      * @param {Number} start The record index to start at (defaults to 0)
23246      * @param {Number} end The last record index to include (defaults to length - 1)
23247      * @return {Number} The sum
23248      */
23249     sum : function(property, start, end){
23250         var rs = this.data.items, v = 0;
23251         start = start || 0;
23252         end = (end || end === 0) ? end : rs.length-1;
23253
23254         for(var i = start; i <= end; i++){
23255             v += (rs[i].data[property] || 0);
23256         }
23257         return v;
23258     },
23259
23260     /**
23261      * Filter the records by a specified property.
23262      * @param {String} field A field on your records
23263      * @param {String/RegExp} value Either a string that the field
23264      * should start with or a RegExp to test against the field
23265      * @param {Boolean} anyMatch True to match any part not just the beginning
23266      */
23267     filter : function(property, value, anyMatch){
23268         var fn = this.createFilterFn(property, value, anyMatch);
23269         return fn ? this.filterBy(fn) : this.clearFilter();
23270     },
23271
23272     /**
23273      * Filter by a function. The specified function will be called with each
23274      * record in this data source. If the function returns true the record is included,
23275      * otherwise it is filtered.
23276      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23277      * @param {Object} scope (optional) The scope of the function (defaults to this)
23278      */
23279     filterBy : function(fn, scope){
23280         this.snapshot = this.snapshot || this.data;
23281         this.data = this.queryBy(fn, scope||this);
23282         this.fireEvent("datachanged", this);
23283     },
23284
23285     /**
23286      * Query the records by a specified property.
23287      * @param {String} field A field on your records
23288      * @param {String/RegExp} value Either a string that the field
23289      * should start with or a RegExp to test against the field
23290      * @param {Boolean} anyMatch True to match any part not just the beginning
23291      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23292      */
23293     query : function(property, value, anyMatch){
23294         var fn = this.createFilterFn(property, value, anyMatch);
23295         return fn ? this.queryBy(fn) : this.data.clone();
23296     },
23297
23298     /**
23299      * Query by a function. The specified function will be called with each
23300      * record in this data source. If the function returns true the record is included
23301      * in the results.
23302      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23303      * @param {Object} scope (optional) The scope of the function (defaults to this)
23304       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23305      **/
23306     queryBy : function(fn, scope){
23307         var data = this.snapshot || this.data;
23308         return data.filterBy(fn, scope||this);
23309     },
23310
23311     /**
23312      * Collects unique values for a particular dataIndex from this store.
23313      * @param {String} dataIndex The property to collect
23314      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23315      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23316      * @return {Array} An array of the unique values
23317      **/
23318     collect : function(dataIndex, allowNull, bypassFilter){
23319         var d = (bypassFilter === true && this.snapshot) ?
23320                 this.snapshot.items : this.data.items;
23321         var v, sv, r = [], l = {};
23322         for(var i = 0, len = d.length; i < len; i++){
23323             v = d[i].data[dataIndex];
23324             sv = String(v);
23325             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23326                 l[sv] = true;
23327                 r[r.length] = v;
23328             }
23329         }
23330         return r;
23331     },
23332
23333     /**
23334      * Revert to a view of the Record cache with no filtering applied.
23335      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
23336      */
23337     clearFilter : function(suppressEvent){
23338         if(this.snapshot && this.snapshot != this.data){
23339             this.data = this.snapshot;
23340             delete this.snapshot;
23341             if(suppressEvent !== true){
23342                 this.fireEvent("datachanged", this);
23343             }
23344         }
23345     },
23346
23347     // private
23348     afterEdit : function(record){
23349         if(this.modified.indexOf(record) == -1){
23350             this.modified.push(record);
23351         }
23352         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
23353     },
23354     
23355     // private
23356     afterReject : function(record){
23357         this.modified.remove(record);
23358         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
23359     },
23360
23361     // private
23362     afterCommit : function(record){
23363         this.modified.remove(record);
23364         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
23365     },
23366
23367     /**
23368      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
23369      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
23370      */
23371     commitChanges : function(){
23372         var m = this.modified.slice(0);
23373         this.modified = [];
23374         for(var i = 0, len = m.length; i < len; i++){
23375             m[i].commit();
23376         }
23377     },
23378
23379     /**
23380      * Cancel outstanding changes on all changed records.
23381      */
23382     rejectChanges : function(){
23383         var m = this.modified.slice(0);
23384         this.modified = [];
23385         for(var i = 0, len = m.length; i < len; i++){
23386             m[i].reject();
23387         }
23388     },
23389
23390     onMetaChange : function(meta, rtype, o){
23391         this.recordType = rtype;
23392         this.fields = rtype.prototype.fields;
23393         delete this.snapshot;
23394         this.sortInfo = meta.sortInfo || this.sortInfo;
23395         this.modified = [];
23396         this.fireEvent('metachange', this, this.reader.meta);
23397     },
23398     
23399     moveIndex : function(data, type)
23400     {
23401         var index = this.indexOf(data);
23402         
23403         var newIndex = index + type;
23404         
23405         this.remove(data);
23406         
23407         this.insert(newIndex, data);
23408         
23409     }
23410 });/*
23411  * Based on:
23412  * Ext JS Library 1.1.1
23413  * Copyright(c) 2006-2007, Ext JS, LLC.
23414  *
23415  * Originally Released Under LGPL - original licence link has changed is not relivant.
23416  *
23417  * Fork - LGPL
23418  * <script type="text/javascript">
23419  */
23420
23421 /**
23422  * @class Roo.data.SimpleStore
23423  * @extends Roo.data.Store
23424  * Small helper class to make creating Stores from Array data easier.
23425  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
23426  * @cfg {Array} fields An array of field definition objects, or field name strings.
23427  * @cfg {Array} data The multi-dimensional array of data
23428  * @constructor
23429  * @param {Object} config
23430  */
23431 Roo.data.SimpleStore = function(config){
23432     Roo.data.SimpleStore.superclass.constructor.call(this, {
23433         isLocal : true,
23434         reader: new Roo.data.ArrayReader({
23435                 id: config.id
23436             },
23437             Roo.data.Record.create(config.fields)
23438         ),
23439         proxy : new Roo.data.MemoryProxy(config.data)
23440     });
23441     this.load();
23442 };
23443 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
23444  * Based on:
23445  * Ext JS Library 1.1.1
23446  * Copyright(c) 2006-2007, Ext JS, LLC.
23447  *
23448  * Originally Released Under LGPL - original licence link has changed is not relivant.
23449  *
23450  * Fork - LGPL
23451  * <script type="text/javascript">
23452  */
23453
23454 /**
23455 /**
23456  * @extends Roo.data.Store
23457  * @class Roo.data.JsonStore
23458  * Small helper class to make creating Stores for JSON data easier. <br/>
23459 <pre><code>
23460 var store = new Roo.data.JsonStore({
23461     url: 'get-images.php',
23462     root: 'images',
23463     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
23464 });
23465 </code></pre>
23466  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
23467  * JsonReader and HttpProxy (unless inline data is provided).</b>
23468  * @cfg {Array} fields An array of field definition objects, or field name strings.
23469  * @constructor
23470  * @param {Object} config
23471  */
23472 Roo.data.JsonStore = function(c){
23473     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
23474         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
23475         reader: new Roo.data.JsonReader(c, c.fields)
23476     }));
23477 };
23478 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
23479  * Based on:
23480  * Ext JS Library 1.1.1
23481  * Copyright(c) 2006-2007, Ext JS, LLC.
23482  *
23483  * Originally Released Under LGPL - original licence link has changed is not relivant.
23484  *
23485  * Fork - LGPL
23486  * <script type="text/javascript">
23487  */
23488
23489  
23490 Roo.data.Field = function(config){
23491     if(typeof config == "string"){
23492         config = {name: config};
23493     }
23494     Roo.apply(this, config);
23495     
23496     if(!this.type){
23497         this.type = "auto";
23498     }
23499     
23500     var st = Roo.data.SortTypes;
23501     // named sortTypes are supported, here we look them up
23502     if(typeof this.sortType == "string"){
23503         this.sortType = st[this.sortType];
23504     }
23505     
23506     // set default sortType for strings and dates
23507     if(!this.sortType){
23508         switch(this.type){
23509             case "string":
23510                 this.sortType = st.asUCString;
23511                 break;
23512             case "date":
23513                 this.sortType = st.asDate;
23514                 break;
23515             default:
23516                 this.sortType = st.none;
23517         }
23518     }
23519
23520     // define once
23521     var stripRe = /[\$,%]/g;
23522
23523     // prebuilt conversion function for this field, instead of
23524     // switching every time we're reading a value
23525     if(!this.convert){
23526         var cv, dateFormat = this.dateFormat;
23527         switch(this.type){
23528             case "":
23529             case "auto":
23530             case undefined:
23531                 cv = function(v){ return v; };
23532                 break;
23533             case "string":
23534                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
23535                 break;
23536             case "int":
23537                 cv = function(v){
23538                     return v !== undefined && v !== null && v !== '' ?
23539                            parseInt(String(v).replace(stripRe, ""), 10) : '';
23540                     };
23541                 break;
23542             case "float":
23543                 cv = function(v){
23544                     return v !== undefined && v !== null && v !== '' ?
23545                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
23546                     };
23547                 break;
23548             case "bool":
23549             case "boolean":
23550                 cv = function(v){ return v === true || v === "true" || v == 1; };
23551                 break;
23552             case "date":
23553                 cv = function(v){
23554                     if(!v){
23555                         return '';
23556                     }
23557                     if(v instanceof Date){
23558                         return v;
23559                     }
23560                     if(dateFormat){
23561                         if(dateFormat == "timestamp"){
23562                             return new Date(v*1000);
23563                         }
23564                         return Date.parseDate(v, dateFormat);
23565                     }
23566                     var parsed = Date.parse(v);
23567                     return parsed ? new Date(parsed) : null;
23568                 };
23569              break;
23570             
23571         }
23572         this.convert = cv;
23573     }
23574 };
23575
23576 Roo.data.Field.prototype = {
23577     dateFormat: null,
23578     defaultValue: "",
23579     mapping: null,
23580     sortType : null,
23581     sortDir : "ASC"
23582 };/*
23583  * Based on:
23584  * Ext JS Library 1.1.1
23585  * Copyright(c) 2006-2007, Ext JS, LLC.
23586  *
23587  * Originally Released Under LGPL - original licence link has changed is not relivant.
23588  *
23589  * Fork - LGPL
23590  * <script type="text/javascript">
23591  */
23592  
23593 // Base class for reading structured data from a data source.  This class is intended to be
23594 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
23595
23596 /**
23597  * @class Roo.data.DataReader
23598  * Base class for reading structured data from a data source.  This class is intended to be
23599  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
23600  */
23601
23602 Roo.data.DataReader = function(meta, recordType){
23603     
23604     this.meta = meta;
23605     
23606     this.recordType = recordType instanceof Array ? 
23607         Roo.data.Record.create(recordType) : recordType;
23608 };
23609
23610 Roo.data.DataReader.prototype = {
23611      /**
23612      * Create an empty record
23613      * @param {Object} data (optional) - overlay some values
23614      * @return {Roo.data.Record} record created.
23615      */
23616     newRow :  function(d) {
23617         var da =  {};
23618         this.recordType.prototype.fields.each(function(c) {
23619             switch( c.type) {
23620                 case 'int' : da[c.name] = 0; break;
23621                 case 'date' : da[c.name] = new Date(); break;
23622                 case 'float' : da[c.name] = 0.0; break;
23623                 case 'boolean' : da[c.name] = false; break;
23624                 default : da[c.name] = ""; break;
23625             }
23626             
23627         });
23628         return new this.recordType(Roo.apply(da, d));
23629     }
23630     
23631 };/*
23632  * Based on:
23633  * Ext JS Library 1.1.1
23634  * Copyright(c) 2006-2007, Ext JS, LLC.
23635  *
23636  * Originally Released Under LGPL - original licence link has changed is not relivant.
23637  *
23638  * Fork - LGPL
23639  * <script type="text/javascript">
23640  */
23641
23642 /**
23643  * @class Roo.data.DataProxy
23644  * @extends Roo.data.Observable
23645  * This class is an abstract base class for implementations which provide retrieval of
23646  * unformatted data objects.<br>
23647  * <p>
23648  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
23649  * (of the appropriate type which knows how to parse the data object) to provide a block of
23650  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
23651  * <p>
23652  * Custom implementations must implement the load method as described in
23653  * {@link Roo.data.HttpProxy#load}.
23654  */
23655 Roo.data.DataProxy = function(){
23656     this.addEvents({
23657         /**
23658          * @event beforeload
23659          * Fires before a network request is made to retrieve a data object.
23660          * @param {Object} This DataProxy object.
23661          * @param {Object} params The params parameter to the load function.
23662          */
23663         beforeload : true,
23664         /**
23665          * @event load
23666          * Fires before the load method's callback is called.
23667          * @param {Object} This DataProxy object.
23668          * @param {Object} o The data object.
23669          * @param {Object} arg The callback argument object passed to the load function.
23670          */
23671         load : true,
23672         /**
23673          * @event loadexception
23674          * Fires if an Exception occurs during data retrieval.
23675          * @param {Object} This DataProxy object.
23676          * @param {Object} o The data object.
23677          * @param {Object} arg The callback argument object passed to the load function.
23678          * @param {Object} e The Exception.
23679          */
23680         loadexception : true
23681     });
23682     Roo.data.DataProxy.superclass.constructor.call(this);
23683 };
23684
23685 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
23686
23687     /**
23688      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
23689      */
23690 /*
23691  * Based on:
23692  * Ext JS Library 1.1.1
23693  * Copyright(c) 2006-2007, Ext JS, LLC.
23694  *
23695  * Originally Released Under LGPL - original licence link has changed is not relivant.
23696  *
23697  * Fork - LGPL
23698  * <script type="text/javascript">
23699  */
23700 /**
23701  * @class Roo.data.MemoryProxy
23702  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
23703  * to the Reader when its load method is called.
23704  * @constructor
23705  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
23706  */
23707 Roo.data.MemoryProxy = function(data){
23708     if (data.data) {
23709         data = data.data;
23710     }
23711     Roo.data.MemoryProxy.superclass.constructor.call(this);
23712     this.data = data;
23713 };
23714
23715 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
23716     
23717     /**
23718      * Load data from the requested source (in this case an in-memory
23719      * data object passed to the constructor), read the data object into
23720      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23721      * process that block using the passed callback.
23722      * @param {Object} params This parameter is not used by the MemoryProxy class.
23723      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23724      * object into a block of Roo.data.Records.
23725      * @param {Function} callback The function into which to pass the block of Roo.data.records.
23726      * The function must be passed <ul>
23727      * <li>The Record block object</li>
23728      * <li>The "arg" argument from the load function</li>
23729      * <li>A boolean success indicator</li>
23730      * </ul>
23731      * @param {Object} scope The scope in which to call the callback
23732      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23733      */
23734     load : function(params, reader, callback, scope, arg){
23735         params = params || {};
23736         var result;
23737         try {
23738             result = reader.readRecords(this.data);
23739         }catch(e){
23740             this.fireEvent("loadexception", this, arg, null, e);
23741             callback.call(scope, null, arg, false);
23742             return;
23743         }
23744         callback.call(scope, result, arg, true);
23745     },
23746     
23747     // private
23748     update : function(params, records){
23749         
23750     }
23751 });/*
23752  * Based on:
23753  * Ext JS Library 1.1.1
23754  * Copyright(c) 2006-2007, Ext JS, LLC.
23755  *
23756  * Originally Released Under LGPL - original licence link has changed is not relivant.
23757  *
23758  * Fork - LGPL
23759  * <script type="text/javascript">
23760  */
23761 /**
23762  * @class Roo.data.HttpProxy
23763  * @extends Roo.data.DataProxy
23764  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
23765  * configured to reference a certain URL.<br><br>
23766  * <p>
23767  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
23768  * from which the running page was served.<br><br>
23769  * <p>
23770  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
23771  * <p>
23772  * Be aware that to enable the browser to parse an XML document, the server must set
23773  * the Content-Type header in the HTTP response to "text/xml".
23774  * @constructor
23775  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
23776  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
23777  * will be used to make the request.
23778  */
23779 Roo.data.HttpProxy = function(conn){
23780     Roo.data.HttpProxy.superclass.constructor.call(this);
23781     // is conn a conn config or a real conn?
23782     this.conn = conn;
23783     this.useAjax = !conn || !conn.events;
23784   
23785 };
23786
23787 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
23788     // thse are take from connection...
23789     
23790     /**
23791      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
23792      */
23793     /**
23794      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
23795      * extra parameters to each request made by this object. (defaults to undefined)
23796      */
23797     /**
23798      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
23799      *  to each request made by this object. (defaults to undefined)
23800      */
23801     /**
23802      * @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)
23803      */
23804     /**
23805      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
23806      */
23807      /**
23808      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
23809      * @type Boolean
23810      */
23811   
23812
23813     /**
23814      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
23815      * @type Boolean
23816      */
23817     /**
23818      * Return the {@link Roo.data.Connection} object being used by this Proxy.
23819      * @return {Connection} The Connection object. This object may be used to subscribe to events on
23820      * a finer-grained basis than the DataProxy events.
23821      */
23822     getConnection : function(){
23823         return this.useAjax ? Roo.Ajax : this.conn;
23824     },
23825
23826     /**
23827      * Load data from the configured {@link Roo.data.Connection}, read the data object into
23828      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
23829      * process that block using the passed callback.
23830      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23831      * for the request to the remote server.
23832      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23833      * object into a block of Roo.data.Records.
23834      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23835      * The function must be passed <ul>
23836      * <li>The Record block object</li>
23837      * <li>The "arg" argument from the load function</li>
23838      * <li>A boolean success indicator</li>
23839      * </ul>
23840      * @param {Object} scope The scope in which to call the callback
23841      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23842      */
23843     load : function(params, reader, callback, scope, arg){
23844         if(this.fireEvent("beforeload", this, params) !== false){
23845             var  o = {
23846                 params : params || {},
23847                 request: {
23848                     callback : callback,
23849                     scope : scope,
23850                     arg : arg
23851                 },
23852                 reader: reader,
23853                 callback : this.loadResponse,
23854                 scope: this
23855             };
23856             if(this.useAjax){
23857                 Roo.applyIf(o, this.conn);
23858                 if(this.activeRequest){
23859                     Roo.Ajax.abort(this.activeRequest);
23860                 }
23861                 this.activeRequest = Roo.Ajax.request(o);
23862             }else{
23863                 this.conn.request(o);
23864             }
23865         }else{
23866             callback.call(scope||this, null, arg, false);
23867         }
23868     },
23869
23870     // private
23871     loadResponse : function(o, success, response){
23872         delete this.activeRequest;
23873         if(!success){
23874             this.fireEvent("loadexception", this, o, response);
23875             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23876             return;
23877         }
23878         var result;
23879         try {
23880             result = o.reader.read(response);
23881         }catch(e){
23882             this.fireEvent("loadexception", this, o, response, e);
23883             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23884             return;
23885         }
23886         
23887         this.fireEvent("load", this, o, o.request.arg);
23888         o.request.callback.call(o.request.scope, result, o.request.arg, true);
23889     },
23890
23891     // private
23892     update : function(dataSet){
23893
23894     },
23895
23896     // private
23897     updateResponse : function(dataSet){
23898
23899     }
23900 });/*
23901  * Based on:
23902  * Ext JS Library 1.1.1
23903  * Copyright(c) 2006-2007, Ext JS, LLC.
23904  *
23905  * Originally Released Under LGPL - original licence link has changed is not relivant.
23906  *
23907  * Fork - LGPL
23908  * <script type="text/javascript">
23909  */
23910
23911 /**
23912  * @class Roo.data.ScriptTagProxy
23913  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
23914  * other than the originating domain of the running page.<br><br>
23915  * <p>
23916  * <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
23917  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
23918  * <p>
23919  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
23920  * source code that is used as the source inside a &lt;script> tag.<br><br>
23921  * <p>
23922  * In order for the browser to process the returned data, the server must wrap the data object
23923  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
23924  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
23925  * depending on whether the callback name was passed:
23926  * <p>
23927  * <pre><code>
23928 boolean scriptTag = false;
23929 String cb = request.getParameter("callback");
23930 if (cb != null) {
23931     scriptTag = true;
23932     response.setContentType("text/javascript");
23933 } else {
23934     response.setContentType("application/x-json");
23935 }
23936 Writer out = response.getWriter();
23937 if (scriptTag) {
23938     out.write(cb + "(");
23939 }
23940 out.print(dataBlock.toJsonString());
23941 if (scriptTag) {
23942     out.write(");");
23943 }
23944 </pre></code>
23945  *
23946  * @constructor
23947  * @param {Object} config A configuration object.
23948  */
23949 Roo.data.ScriptTagProxy = function(config){
23950     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
23951     Roo.apply(this, config);
23952     this.head = document.getElementsByTagName("head")[0];
23953 };
23954
23955 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
23956
23957 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
23958     /**
23959      * @cfg {String} url The URL from which to request the data object.
23960      */
23961     /**
23962      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
23963      */
23964     timeout : 30000,
23965     /**
23966      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
23967      * the server the name of the callback function set up by the load call to process the returned data object.
23968      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
23969      * javascript output which calls this named function passing the data object as its only parameter.
23970      */
23971     callbackParam : "callback",
23972     /**
23973      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
23974      * name to the request.
23975      */
23976     nocache : true,
23977
23978     /**
23979      * Load data from the configured URL, read the data object into
23980      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23981      * process that block using the passed callback.
23982      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23983      * for the request to the remote server.
23984      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23985      * object into a block of Roo.data.Records.
23986      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23987      * The function must be passed <ul>
23988      * <li>The Record block object</li>
23989      * <li>The "arg" argument from the load function</li>
23990      * <li>A boolean success indicator</li>
23991      * </ul>
23992      * @param {Object} scope The scope in which to call the callback
23993      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23994      */
23995     load : function(params, reader, callback, scope, arg){
23996         if(this.fireEvent("beforeload", this, params) !== false){
23997
23998             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
23999
24000             var url = this.url;
24001             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24002             if(this.nocache){
24003                 url += "&_dc=" + (new Date().getTime());
24004             }
24005             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24006             var trans = {
24007                 id : transId,
24008                 cb : "stcCallback"+transId,
24009                 scriptId : "stcScript"+transId,
24010                 params : params,
24011                 arg : arg,
24012                 url : url,
24013                 callback : callback,
24014                 scope : scope,
24015                 reader : reader
24016             };
24017             var conn = this;
24018
24019             window[trans.cb] = function(o){
24020                 conn.handleResponse(o, trans);
24021             };
24022
24023             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24024
24025             if(this.autoAbort !== false){
24026                 this.abort();
24027             }
24028
24029             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24030
24031             var script = document.createElement("script");
24032             script.setAttribute("src", url);
24033             script.setAttribute("type", "text/javascript");
24034             script.setAttribute("id", trans.scriptId);
24035             this.head.appendChild(script);
24036
24037             this.trans = trans;
24038         }else{
24039             callback.call(scope||this, null, arg, false);
24040         }
24041     },
24042
24043     // private
24044     isLoading : function(){
24045         return this.trans ? true : false;
24046     },
24047
24048     /**
24049      * Abort the current server request.
24050      */
24051     abort : function(){
24052         if(this.isLoading()){
24053             this.destroyTrans(this.trans);
24054         }
24055     },
24056
24057     // private
24058     destroyTrans : function(trans, isLoaded){
24059         this.head.removeChild(document.getElementById(trans.scriptId));
24060         clearTimeout(trans.timeoutId);
24061         if(isLoaded){
24062             window[trans.cb] = undefined;
24063             try{
24064                 delete window[trans.cb];
24065             }catch(e){}
24066         }else{
24067             // if hasn't been loaded, wait for load to remove it to prevent script error
24068             window[trans.cb] = function(){
24069                 window[trans.cb] = undefined;
24070                 try{
24071                     delete window[trans.cb];
24072                 }catch(e){}
24073             };
24074         }
24075     },
24076
24077     // private
24078     handleResponse : function(o, trans){
24079         this.trans = false;
24080         this.destroyTrans(trans, true);
24081         var result;
24082         try {
24083             result = trans.reader.readRecords(o);
24084         }catch(e){
24085             this.fireEvent("loadexception", this, o, trans.arg, e);
24086             trans.callback.call(trans.scope||window, null, trans.arg, false);
24087             return;
24088         }
24089         this.fireEvent("load", this, o, trans.arg);
24090         trans.callback.call(trans.scope||window, result, trans.arg, true);
24091     },
24092
24093     // private
24094     handleFailure : function(trans){
24095         this.trans = false;
24096         this.destroyTrans(trans, false);
24097         this.fireEvent("loadexception", this, null, trans.arg);
24098         trans.callback.call(trans.scope||window, null, trans.arg, false);
24099     }
24100 });/*
24101  * Based on:
24102  * Ext JS Library 1.1.1
24103  * Copyright(c) 2006-2007, Ext JS, LLC.
24104  *
24105  * Originally Released Under LGPL - original licence link has changed is not relivant.
24106  *
24107  * Fork - LGPL
24108  * <script type="text/javascript">
24109  */
24110
24111 /**
24112  * @class Roo.data.JsonReader
24113  * @extends Roo.data.DataReader
24114  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24115  * based on mappings in a provided Roo.data.Record constructor.
24116  * 
24117  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24118  * in the reply previously. 
24119  * 
24120  * <p>
24121  * Example code:
24122  * <pre><code>
24123 var RecordDef = Roo.data.Record.create([
24124     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24125     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24126 ]);
24127 var myReader = new Roo.data.JsonReader({
24128     totalProperty: "results",    // The property which contains the total dataset size (optional)
24129     root: "rows",                // The property which contains an Array of row objects
24130     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24131 }, RecordDef);
24132 </code></pre>
24133  * <p>
24134  * This would consume a JSON file like this:
24135  * <pre><code>
24136 { 'results': 2, 'rows': [
24137     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24138     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24139 }
24140 </code></pre>
24141  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24142  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24143  * paged from the remote server.
24144  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24145  * @cfg {String} root name of the property which contains the Array of row objects.
24146  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24147  * @cfg {Array} fields Array of field definition objects
24148  * @constructor
24149  * Create a new JsonReader
24150  * @param {Object} meta Metadata configuration options
24151  * @param {Object} recordType Either an Array of field definition objects,
24152  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24153  */
24154 Roo.data.JsonReader = function(meta, recordType){
24155     
24156     meta = meta || {};
24157     // set some defaults:
24158     Roo.applyIf(meta, {
24159         totalProperty: 'total',
24160         successProperty : 'success',
24161         root : 'data',
24162         id : 'id'
24163     });
24164     
24165     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24166 };
24167 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24168     
24169     /**
24170      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24171      * Used by Store query builder to append _requestMeta to params.
24172      * 
24173      */
24174     metaFromRemote : false,
24175     /**
24176      * This method is only used by a DataProxy which has retrieved data from a remote server.
24177      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24178      * @return {Object} data A data block which is used by an Roo.data.Store object as
24179      * a cache of Roo.data.Records.
24180      */
24181     read : function(response){
24182         var json = response.responseText;
24183        
24184         var o = /* eval:var:o */ eval("("+json+")");
24185         if(!o) {
24186             throw {message: "JsonReader.read: Json object not found"};
24187         }
24188         
24189         if(o.metaData){
24190             
24191             delete this.ef;
24192             this.metaFromRemote = true;
24193             this.meta = o.metaData;
24194             this.recordType = Roo.data.Record.create(o.metaData.fields);
24195             this.onMetaChange(this.meta, this.recordType, o);
24196         }
24197         return this.readRecords(o);
24198     },
24199
24200     // private function a store will implement
24201     onMetaChange : function(meta, recordType, o){
24202
24203     },
24204
24205     /**
24206          * @ignore
24207          */
24208     simpleAccess: function(obj, subsc) {
24209         return obj[subsc];
24210     },
24211
24212         /**
24213          * @ignore
24214          */
24215     getJsonAccessor: function(){
24216         var re = /[\[\.]/;
24217         return function(expr) {
24218             try {
24219                 return(re.test(expr))
24220                     ? new Function("obj", "return obj." + expr)
24221                     : function(obj){
24222                         return obj[expr];
24223                     };
24224             } catch(e){}
24225             return Roo.emptyFn;
24226         };
24227     }(),
24228
24229     /**
24230      * Create a data block containing Roo.data.Records from an XML document.
24231      * @param {Object} o An object which contains an Array of row objects in the property specified
24232      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24233      * which contains the total size of the dataset.
24234      * @return {Object} data A data block which is used by an Roo.data.Store object as
24235      * a cache of Roo.data.Records.
24236      */
24237     readRecords : function(o){
24238         /**
24239          * After any data loads, the raw JSON data is available for further custom processing.
24240          * @type Object
24241          */
24242         this.o = o;
24243         var s = this.meta, Record = this.recordType,
24244             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24245
24246 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24247         if (!this.ef) {
24248             if(s.totalProperty) {
24249                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24250                 }
24251                 if(s.successProperty) {
24252                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24253                 }
24254                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24255                 if (s.id) {
24256                         var g = this.getJsonAccessor(s.id);
24257                         this.getId = function(rec) {
24258                                 var r = g(rec);  
24259                                 return (r === undefined || r === "") ? null : r;
24260                         };
24261                 } else {
24262                         this.getId = function(){return null;};
24263                 }
24264             this.ef = [];
24265             for(var jj = 0; jj < fl; jj++){
24266                 f = fi[jj];
24267                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24268                 this.ef[jj] = this.getJsonAccessor(map);
24269             }
24270         }
24271
24272         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24273         if(s.totalProperty){
24274             var vt = parseInt(this.getTotal(o), 10);
24275             if(!isNaN(vt)){
24276                 totalRecords = vt;
24277             }
24278         }
24279         if(s.successProperty){
24280             var vs = this.getSuccess(o);
24281             if(vs === false || vs === 'false'){
24282                 success = false;
24283             }
24284         }
24285         var records = [];
24286         for(var i = 0; i < c; i++){
24287                 var n = root[i];
24288             var values = {};
24289             var id = this.getId(n);
24290             for(var j = 0; j < fl; j++){
24291                 f = fi[j];
24292             var v = this.ef[j](n);
24293             if (!f.convert) {
24294                 Roo.log('missing convert for ' + f.name);
24295                 Roo.log(f);
24296                 continue;
24297             }
24298             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24299             }
24300             var record = new Record(values, id);
24301             record.json = n;
24302             records[i] = record;
24303         }
24304         return {
24305             raw : o,
24306             success : success,
24307             records : records,
24308             totalRecords : totalRecords
24309         };
24310     }
24311 });/*
24312  * Based on:
24313  * Ext JS Library 1.1.1
24314  * Copyright(c) 2006-2007, Ext JS, LLC.
24315  *
24316  * Originally Released Under LGPL - original licence link has changed is not relivant.
24317  *
24318  * Fork - LGPL
24319  * <script type="text/javascript">
24320  */
24321
24322 /**
24323  * @class Roo.data.XmlReader
24324  * @extends Roo.data.DataReader
24325  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
24326  * based on mappings in a provided Roo.data.Record constructor.<br><br>
24327  * <p>
24328  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
24329  * header in the HTTP response must be set to "text/xml".</em>
24330  * <p>
24331  * Example code:
24332  * <pre><code>
24333 var RecordDef = Roo.data.Record.create([
24334    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24335    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24336 ]);
24337 var myReader = new Roo.data.XmlReader({
24338    totalRecords: "results", // The element which contains the total dataset size (optional)
24339    record: "row",           // The repeated element which contains row information
24340    id: "id"                 // The element within the row that provides an ID for the record (optional)
24341 }, RecordDef);
24342 </code></pre>
24343  * <p>
24344  * This would consume an XML file like this:
24345  * <pre><code>
24346 &lt;?xml?>
24347 &lt;dataset>
24348  &lt;results>2&lt;/results>
24349  &lt;row>
24350    &lt;id>1&lt;/id>
24351    &lt;name>Bill&lt;/name>
24352    &lt;occupation>Gardener&lt;/occupation>
24353  &lt;/row>
24354  &lt;row>
24355    &lt;id>2&lt;/id>
24356    &lt;name>Ben&lt;/name>
24357    &lt;occupation>Horticulturalist&lt;/occupation>
24358  &lt;/row>
24359 &lt;/dataset>
24360 </code></pre>
24361  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
24362  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24363  * paged from the remote server.
24364  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
24365  * @cfg {String} success The DomQuery path to the success attribute used by forms.
24366  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
24367  * a record identifier value.
24368  * @constructor
24369  * Create a new XmlReader
24370  * @param {Object} meta Metadata configuration options
24371  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
24372  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
24373  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
24374  */
24375 Roo.data.XmlReader = function(meta, recordType){
24376     meta = meta || {};
24377     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24378 };
24379 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
24380     /**
24381      * This method is only used by a DataProxy which has retrieved data from a remote server.
24382          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
24383          * to contain a method called 'responseXML' that returns an XML document object.
24384      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24385      * a cache of Roo.data.Records.
24386      */
24387     read : function(response){
24388         var doc = response.responseXML;
24389         if(!doc) {
24390             throw {message: "XmlReader.read: XML Document not available"};
24391         }
24392         return this.readRecords(doc);
24393     },
24394
24395     /**
24396      * Create a data block containing Roo.data.Records from an XML document.
24397          * @param {Object} doc A parsed XML document.
24398      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24399      * a cache of Roo.data.Records.
24400      */
24401     readRecords : function(doc){
24402         /**
24403          * After any data loads/reads, the raw XML Document is available for further custom processing.
24404          * @type XMLDocument
24405          */
24406         this.xmlData = doc;
24407         var root = doc.documentElement || doc;
24408         var q = Roo.DomQuery;
24409         var recordType = this.recordType, fields = recordType.prototype.fields;
24410         var sid = this.meta.id;
24411         var totalRecords = 0, success = true;
24412         if(this.meta.totalRecords){
24413             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
24414         }
24415         
24416         if(this.meta.success){
24417             var sv = q.selectValue(this.meta.success, root, true);
24418             success = sv !== false && sv !== 'false';
24419         }
24420         var records = [];
24421         var ns = q.select(this.meta.record, root);
24422         for(var i = 0, len = ns.length; i < len; i++) {
24423                 var n = ns[i];
24424                 var values = {};
24425                 var id = sid ? q.selectValue(sid, n) : undefined;
24426                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24427                     var f = fields.items[j];
24428                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
24429                     v = f.convert(v);
24430                     values[f.name] = v;
24431                 }
24432                 var record = new recordType(values, id);
24433                 record.node = n;
24434                 records[records.length] = record;
24435             }
24436
24437             return {
24438                 success : success,
24439                 records : records,
24440                 totalRecords : totalRecords || records.length
24441             };
24442     }
24443 });/*
24444  * Based on:
24445  * Ext JS Library 1.1.1
24446  * Copyright(c) 2006-2007, Ext JS, LLC.
24447  *
24448  * Originally Released Under LGPL - original licence link has changed is not relivant.
24449  *
24450  * Fork - LGPL
24451  * <script type="text/javascript">
24452  */
24453
24454 /**
24455  * @class Roo.data.ArrayReader
24456  * @extends Roo.data.DataReader
24457  * Data reader class to create an Array of Roo.data.Record objects from an Array.
24458  * Each element of that Array represents a row of data fields. The
24459  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
24460  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
24461  * <p>
24462  * Example code:.
24463  * <pre><code>
24464 var RecordDef = Roo.data.Record.create([
24465     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
24466     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
24467 ]);
24468 var myReader = new Roo.data.ArrayReader({
24469     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
24470 }, RecordDef);
24471 </code></pre>
24472  * <p>
24473  * This would consume an Array like this:
24474  * <pre><code>
24475 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
24476   </code></pre>
24477  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
24478  * @constructor
24479  * Create a new JsonReader
24480  * @param {Object} meta Metadata configuration options.
24481  * @param {Object} recordType Either an Array of field definition objects
24482  * as specified to {@link Roo.data.Record#create},
24483  * or an {@link Roo.data.Record} object
24484  * created using {@link Roo.data.Record#create}.
24485  */
24486 Roo.data.ArrayReader = function(meta, recordType){
24487     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
24488 };
24489
24490 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
24491     /**
24492      * Create a data block containing Roo.data.Records from an XML document.
24493      * @param {Object} o An Array of row objects which represents the dataset.
24494      * @return {Object} data A data block which is used by an Roo.data.Store object as
24495      * a cache of Roo.data.Records.
24496      */
24497     readRecords : function(o){
24498         var sid = this.meta ? this.meta.id : null;
24499         var recordType = this.recordType, fields = recordType.prototype.fields;
24500         var records = [];
24501         var root = o;
24502             for(var i = 0; i < root.length; i++){
24503                     var n = root[i];
24504                 var values = {};
24505                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
24506                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24507                 var f = fields.items[j];
24508                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
24509                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
24510                 v = f.convert(v);
24511                 values[f.name] = v;
24512             }
24513                 var record = new recordType(values, id);
24514                 record.json = n;
24515                 records[records.length] = record;
24516             }
24517             return {
24518                 records : records,
24519                 totalRecords : records.length
24520             };
24521     }
24522 });/*
24523  * Based on:
24524  * Ext JS Library 1.1.1
24525  * Copyright(c) 2006-2007, Ext JS, LLC.
24526  *
24527  * Originally Released Under LGPL - original licence link has changed is not relivant.
24528  *
24529  * Fork - LGPL
24530  * <script type="text/javascript">
24531  */
24532
24533
24534 /**
24535  * @class Roo.data.Tree
24536  * @extends Roo.util.Observable
24537  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
24538  * in the tree have most standard DOM functionality.
24539  * @constructor
24540  * @param {Node} root (optional) The root node
24541  */
24542 Roo.data.Tree = function(root){
24543    this.nodeHash = {};
24544    /**
24545     * The root node for this tree
24546     * @type Node
24547     */
24548    this.root = null;
24549    if(root){
24550        this.setRootNode(root);
24551    }
24552    this.addEvents({
24553        /**
24554         * @event append
24555         * Fires when a new child node is appended to a node in this tree.
24556         * @param {Tree} tree The owner tree
24557         * @param {Node} parent The parent node
24558         * @param {Node} node The newly appended node
24559         * @param {Number} index The index of the newly appended node
24560         */
24561        "append" : true,
24562        /**
24563         * @event remove
24564         * Fires when a child node is removed from a node in this tree.
24565         * @param {Tree} tree The owner tree
24566         * @param {Node} parent The parent node
24567         * @param {Node} node The child node removed
24568         */
24569        "remove" : true,
24570        /**
24571         * @event move
24572         * Fires when a node is moved to a new location in the tree
24573         * @param {Tree} tree The owner tree
24574         * @param {Node} node The node moved
24575         * @param {Node} oldParent The old parent of this node
24576         * @param {Node} newParent The new parent of this node
24577         * @param {Number} index The index it was moved to
24578         */
24579        "move" : true,
24580        /**
24581         * @event insert
24582         * Fires when a new child node is inserted in a node in this tree.
24583         * @param {Tree} tree The owner tree
24584         * @param {Node} parent The parent node
24585         * @param {Node} node The child node inserted
24586         * @param {Node} refNode The child node the node was inserted before
24587         */
24588        "insert" : true,
24589        /**
24590         * @event beforeappend
24591         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
24592         * @param {Tree} tree The owner tree
24593         * @param {Node} parent The parent node
24594         * @param {Node} node The child node to be appended
24595         */
24596        "beforeappend" : true,
24597        /**
24598         * @event beforeremove
24599         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
24600         * @param {Tree} tree The owner tree
24601         * @param {Node} parent The parent node
24602         * @param {Node} node The child node to be removed
24603         */
24604        "beforeremove" : true,
24605        /**
24606         * @event beforemove
24607         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
24608         * @param {Tree} tree The owner tree
24609         * @param {Node} node The node being moved
24610         * @param {Node} oldParent The parent of the node
24611         * @param {Node} newParent The new parent the node is moving to
24612         * @param {Number} index The index it is being moved to
24613         */
24614        "beforemove" : true,
24615        /**
24616         * @event beforeinsert
24617         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
24618         * @param {Tree} tree The owner tree
24619         * @param {Node} parent The parent node
24620         * @param {Node} node The child node to be inserted
24621         * @param {Node} refNode The child node the node is being inserted before
24622         */
24623        "beforeinsert" : true
24624    });
24625
24626     Roo.data.Tree.superclass.constructor.call(this);
24627 };
24628
24629 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
24630     pathSeparator: "/",
24631
24632     proxyNodeEvent : function(){
24633         return this.fireEvent.apply(this, arguments);
24634     },
24635
24636     /**
24637      * Returns the root node for this tree.
24638      * @return {Node}
24639      */
24640     getRootNode : function(){
24641         return this.root;
24642     },
24643
24644     /**
24645      * Sets the root node for this tree.
24646      * @param {Node} node
24647      * @return {Node}
24648      */
24649     setRootNode : function(node){
24650         this.root = node;
24651         node.ownerTree = this;
24652         node.isRoot = true;
24653         this.registerNode(node);
24654         return node;
24655     },
24656
24657     /**
24658      * Gets a node in this tree by its id.
24659      * @param {String} id
24660      * @return {Node}
24661      */
24662     getNodeById : function(id){
24663         return this.nodeHash[id];
24664     },
24665
24666     registerNode : function(node){
24667         this.nodeHash[node.id] = node;
24668     },
24669
24670     unregisterNode : function(node){
24671         delete this.nodeHash[node.id];
24672     },
24673
24674     toString : function(){
24675         return "[Tree"+(this.id?" "+this.id:"")+"]";
24676     }
24677 });
24678
24679 /**
24680  * @class Roo.data.Node
24681  * @extends Roo.util.Observable
24682  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
24683  * @cfg {String} id The id for this node. If one is not specified, one is generated.
24684  * @constructor
24685  * @param {Object} attributes The attributes/config for the node
24686  */
24687 Roo.data.Node = function(attributes){
24688     /**
24689      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
24690      * @type {Object}
24691      */
24692     this.attributes = attributes || {};
24693     this.leaf = this.attributes.leaf;
24694     /**
24695      * The node id. @type String
24696      */
24697     this.id = this.attributes.id;
24698     if(!this.id){
24699         this.id = Roo.id(null, "ynode-");
24700         this.attributes.id = this.id;
24701     }
24702      
24703     
24704     /**
24705      * All child nodes of this node. @type Array
24706      */
24707     this.childNodes = [];
24708     if(!this.childNodes.indexOf){ // indexOf is a must
24709         this.childNodes.indexOf = function(o){
24710             for(var i = 0, len = this.length; i < len; i++){
24711                 if(this[i] == o) {
24712                     return i;
24713                 }
24714             }
24715             return -1;
24716         };
24717     }
24718     /**
24719      * The parent node for this node. @type Node
24720      */
24721     this.parentNode = null;
24722     /**
24723      * The first direct child node of this node, or null if this node has no child nodes. @type Node
24724      */
24725     this.firstChild = null;
24726     /**
24727      * The last direct child node of this node, or null if this node has no child nodes. @type Node
24728      */
24729     this.lastChild = null;
24730     /**
24731      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
24732      */
24733     this.previousSibling = null;
24734     /**
24735      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
24736      */
24737     this.nextSibling = null;
24738
24739     this.addEvents({
24740        /**
24741         * @event append
24742         * Fires when a new child node is appended
24743         * @param {Tree} tree The owner tree
24744         * @param {Node} this This node
24745         * @param {Node} node The newly appended node
24746         * @param {Number} index The index of the newly appended node
24747         */
24748        "append" : true,
24749        /**
24750         * @event remove
24751         * Fires when a child node is removed
24752         * @param {Tree} tree The owner tree
24753         * @param {Node} this This node
24754         * @param {Node} node The removed node
24755         */
24756        "remove" : true,
24757        /**
24758         * @event move
24759         * Fires when this node is moved to a new location in the tree
24760         * @param {Tree} tree The owner tree
24761         * @param {Node} this This node
24762         * @param {Node} oldParent The old parent of this node
24763         * @param {Node} newParent The new parent of this node
24764         * @param {Number} index The index it was moved to
24765         */
24766        "move" : true,
24767        /**
24768         * @event insert
24769         * Fires when a new child node is inserted.
24770         * @param {Tree} tree The owner tree
24771         * @param {Node} this This node
24772         * @param {Node} node The child node inserted
24773         * @param {Node} refNode The child node the node was inserted before
24774         */
24775        "insert" : true,
24776        /**
24777         * @event beforeappend
24778         * Fires before a new child is appended, return false to cancel the append.
24779         * @param {Tree} tree The owner tree
24780         * @param {Node} this This node
24781         * @param {Node} node The child node to be appended
24782         */
24783        "beforeappend" : true,
24784        /**
24785         * @event beforeremove
24786         * Fires before a child is removed, return false to cancel the remove.
24787         * @param {Tree} tree The owner tree
24788         * @param {Node} this This node
24789         * @param {Node} node The child node to be removed
24790         */
24791        "beforeremove" : true,
24792        /**
24793         * @event beforemove
24794         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
24795         * @param {Tree} tree The owner tree
24796         * @param {Node} this This node
24797         * @param {Node} oldParent The parent of this node
24798         * @param {Node} newParent The new parent this node is moving to
24799         * @param {Number} index The index it is being moved to
24800         */
24801        "beforemove" : true,
24802        /**
24803         * @event beforeinsert
24804         * Fires before a new child is inserted, return false to cancel the insert.
24805         * @param {Tree} tree The owner tree
24806         * @param {Node} this This node
24807         * @param {Node} node The child node to be inserted
24808         * @param {Node} refNode The child node the node is being inserted before
24809         */
24810        "beforeinsert" : true
24811    });
24812     this.listeners = this.attributes.listeners;
24813     Roo.data.Node.superclass.constructor.call(this);
24814 };
24815
24816 Roo.extend(Roo.data.Node, Roo.util.Observable, {
24817     fireEvent : function(evtName){
24818         // first do standard event for this node
24819         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
24820             return false;
24821         }
24822         // then bubble it up to the tree if the event wasn't cancelled
24823         var ot = this.getOwnerTree();
24824         if(ot){
24825             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
24826                 return false;
24827             }
24828         }
24829         return true;
24830     },
24831
24832     /**
24833      * Returns true if this node is a leaf
24834      * @return {Boolean}
24835      */
24836     isLeaf : function(){
24837         return this.leaf === true;
24838     },
24839
24840     // private
24841     setFirstChild : function(node){
24842         this.firstChild = node;
24843     },
24844
24845     //private
24846     setLastChild : function(node){
24847         this.lastChild = node;
24848     },
24849
24850
24851     /**
24852      * Returns true if this node is the last child of its parent
24853      * @return {Boolean}
24854      */
24855     isLast : function(){
24856        return (!this.parentNode ? true : this.parentNode.lastChild == this);
24857     },
24858
24859     /**
24860      * Returns true if this node is the first child of its parent
24861      * @return {Boolean}
24862      */
24863     isFirst : function(){
24864        return (!this.parentNode ? true : this.parentNode.firstChild == this);
24865     },
24866
24867     hasChildNodes : function(){
24868         return !this.isLeaf() && this.childNodes.length > 0;
24869     },
24870
24871     /**
24872      * Insert node(s) as the last child node of this node.
24873      * @param {Node/Array} node The node or Array of nodes to append
24874      * @return {Node} The appended node if single append, or null if an array was passed
24875      */
24876     appendChild : function(node){
24877         var multi = false;
24878         if(node instanceof Array){
24879             multi = node;
24880         }else if(arguments.length > 1){
24881             multi = arguments;
24882         }
24883         // if passed an array or multiple args do them one by one
24884         if(multi){
24885             for(var i = 0, len = multi.length; i < len; i++) {
24886                 this.appendChild(multi[i]);
24887             }
24888         }else{
24889             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
24890                 return false;
24891             }
24892             var index = this.childNodes.length;
24893             var oldParent = node.parentNode;
24894             // it's a move, make sure we move it cleanly
24895             if(oldParent){
24896                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
24897                     return false;
24898                 }
24899                 oldParent.removeChild(node);
24900             }
24901             index = this.childNodes.length;
24902             if(index == 0){
24903                 this.setFirstChild(node);
24904             }
24905             this.childNodes.push(node);
24906             node.parentNode = this;
24907             var ps = this.childNodes[index-1];
24908             if(ps){
24909                 node.previousSibling = ps;
24910                 ps.nextSibling = node;
24911             }else{
24912                 node.previousSibling = null;
24913             }
24914             node.nextSibling = null;
24915             this.setLastChild(node);
24916             node.setOwnerTree(this.getOwnerTree());
24917             this.fireEvent("append", this.ownerTree, this, node, index);
24918             if(oldParent){
24919                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
24920             }
24921             return node;
24922         }
24923     },
24924
24925     /**
24926      * Removes a child node from this node.
24927      * @param {Node} node The node to remove
24928      * @return {Node} The removed node
24929      */
24930     removeChild : function(node){
24931         var index = this.childNodes.indexOf(node);
24932         if(index == -1){
24933             return false;
24934         }
24935         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
24936             return false;
24937         }
24938
24939         // remove it from childNodes collection
24940         this.childNodes.splice(index, 1);
24941
24942         // update siblings
24943         if(node.previousSibling){
24944             node.previousSibling.nextSibling = node.nextSibling;
24945         }
24946         if(node.nextSibling){
24947             node.nextSibling.previousSibling = node.previousSibling;
24948         }
24949
24950         // update child refs
24951         if(this.firstChild == node){
24952             this.setFirstChild(node.nextSibling);
24953         }
24954         if(this.lastChild == node){
24955             this.setLastChild(node.previousSibling);
24956         }
24957
24958         node.setOwnerTree(null);
24959         // clear any references from the node
24960         node.parentNode = null;
24961         node.previousSibling = null;
24962         node.nextSibling = null;
24963         this.fireEvent("remove", this.ownerTree, this, node);
24964         return node;
24965     },
24966
24967     /**
24968      * Inserts the first node before the second node in this nodes childNodes collection.
24969      * @param {Node} node The node to insert
24970      * @param {Node} refNode The node to insert before (if null the node is appended)
24971      * @return {Node} The inserted node
24972      */
24973     insertBefore : function(node, refNode){
24974         if(!refNode){ // like standard Dom, refNode can be null for append
24975             return this.appendChild(node);
24976         }
24977         // nothing to do
24978         if(node == refNode){
24979             return false;
24980         }
24981
24982         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
24983             return false;
24984         }
24985         var index = this.childNodes.indexOf(refNode);
24986         var oldParent = node.parentNode;
24987         var refIndex = index;
24988
24989         // when moving internally, indexes will change after remove
24990         if(oldParent == this && this.childNodes.indexOf(node) < index){
24991             refIndex--;
24992         }
24993
24994         // it's a move, make sure we move it cleanly
24995         if(oldParent){
24996             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
24997                 return false;
24998             }
24999             oldParent.removeChild(node);
25000         }
25001         if(refIndex == 0){
25002             this.setFirstChild(node);
25003         }
25004         this.childNodes.splice(refIndex, 0, node);
25005         node.parentNode = this;
25006         var ps = this.childNodes[refIndex-1];
25007         if(ps){
25008             node.previousSibling = ps;
25009             ps.nextSibling = node;
25010         }else{
25011             node.previousSibling = null;
25012         }
25013         node.nextSibling = refNode;
25014         refNode.previousSibling = node;
25015         node.setOwnerTree(this.getOwnerTree());
25016         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25017         if(oldParent){
25018             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25019         }
25020         return node;
25021     },
25022
25023     /**
25024      * Returns the child node at the specified index.
25025      * @param {Number} index
25026      * @return {Node}
25027      */
25028     item : function(index){
25029         return this.childNodes[index];
25030     },
25031
25032     /**
25033      * Replaces one child node in this node with another.
25034      * @param {Node} newChild The replacement node
25035      * @param {Node} oldChild The node to replace
25036      * @return {Node} The replaced node
25037      */
25038     replaceChild : function(newChild, oldChild){
25039         this.insertBefore(newChild, oldChild);
25040         this.removeChild(oldChild);
25041         return oldChild;
25042     },
25043
25044     /**
25045      * Returns the index of a child node
25046      * @param {Node} node
25047      * @return {Number} The index of the node or -1 if it was not found
25048      */
25049     indexOf : function(child){
25050         return this.childNodes.indexOf(child);
25051     },
25052
25053     /**
25054      * Returns the tree this node is in.
25055      * @return {Tree}
25056      */
25057     getOwnerTree : function(){
25058         // if it doesn't have one, look for one
25059         if(!this.ownerTree){
25060             var p = this;
25061             while(p){
25062                 if(p.ownerTree){
25063                     this.ownerTree = p.ownerTree;
25064                     break;
25065                 }
25066                 p = p.parentNode;
25067             }
25068         }
25069         return this.ownerTree;
25070     },
25071
25072     /**
25073      * Returns depth of this node (the root node has a depth of 0)
25074      * @return {Number}
25075      */
25076     getDepth : function(){
25077         var depth = 0;
25078         var p = this;
25079         while(p.parentNode){
25080             ++depth;
25081             p = p.parentNode;
25082         }
25083         return depth;
25084     },
25085
25086     // private
25087     setOwnerTree : function(tree){
25088         // if it's move, we need to update everyone
25089         if(tree != this.ownerTree){
25090             if(this.ownerTree){
25091                 this.ownerTree.unregisterNode(this);
25092             }
25093             this.ownerTree = tree;
25094             var cs = this.childNodes;
25095             for(var i = 0, len = cs.length; i < len; i++) {
25096                 cs[i].setOwnerTree(tree);
25097             }
25098             if(tree){
25099                 tree.registerNode(this);
25100             }
25101         }
25102     },
25103
25104     /**
25105      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25106      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25107      * @return {String} The path
25108      */
25109     getPath : function(attr){
25110         attr = attr || "id";
25111         var p = this.parentNode;
25112         var b = [this.attributes[attr]];
25113         while(p){
25114             b.unshift(p.attributes[attr]);
25115             p = p.parentNode;
25116         }
25117         var sep = this.getOwnerTree().pathSeparator;
25118         return sep + b.join(sep);
25119     },
25120
25121     /**
25122      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25123      * function call will be the scope provided or the current node. The arguments to the function
25124      * will be the args provided or the current node. If the function returns false at any point,
25125      * the bubble is stopped.
25126      * @param {Function} fn The function to call
25127      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25128      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25129      */
25130     bubble : function(fn, scope, args){
25131         var p = this;
25132         while(p){
25133             if(fn.call(scope || p, args || p) === false){
25134                 break;
25135             }
25136             p = p.parentNode;
25137         }
25138     },
25139
25140     /**
25141      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25142      * function call will be the scope provided or the current node. The arguments to the function
25143      * will be the args provided or the current node. If the function returns false at any point,
25144      * the cascade is stopped on that branch.
25145      * @param {Function} fn The function to call
25146      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25147      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25148      */
25149     cascade : function(fn, scope, args){
25150         if(fn.call(scope || this, args || this) !== false){
25151             var cs = this.childNodes;
25152             for(var i = 0, len = cs.length; i < len; i++) {
25153                 cs[i].cascade(fn, scope, args);
25154             }
25155         }
25156     },
25157
25158     /**
25159      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25160      * function call will be the scope provided or the current node. The arguments to the function
25161      * will be the args provided or the current node. If the function returns false at any point,
25162      * the iteration stops.
25163      * @param {Function} fn The function to call
25164      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25165      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25166      */
25167     eachChild : function(fn, scope, args){
25168         var cs = this.childNodes;
25169         for(var i = 0, len = cs.length; i < len; i++) {
25170                 if(fn.call(scope || this, args || cs[i]) === false){
25171                     break;
25172                 }
25173         }
25174     },
25175
25176     /**
25177      * Finds the first child that has the attribute with the specified value.
25178      * @param {String} attribute The attribute name
25179      * @param {Mixed} value The value to search for
25180      * @return {Node} The found child or null if none was found
25181      */
25182     findChild : function(attribute, value){
25183         var cs = this.childNodes;
25184         for(var i = 0, len = cs.length; i < len; i++) {
25185                 if(cs[i].attributes[attribute] == value){
25186                     return cs[i];
25187                 }
25188         }
25189         return null;
25190     },
25191
25192     /**
25193      * Finds the first child by a custom function. The child matches if the function passed
25194      * returns true.
25195      * @param {Function} fn
25196      * @param {Object} scope (optional)
25197      * @return {Node} The found child or null if none was found
25198      */
25199     findChildBy : function(fn, scope){
25200         var cs = this.childNodes;
25201         for(var i = 0, len = cs.length; i < len; i++) {
25202                 if(fn.call(scope||cs[i], cs[i]) === true){
25203                     return cs[i];
25204                 }
25205         }
25206         return null;
25207     },
25208
25209     /**
25210      * Sorts this nodes children using the supplied sort function
25211      * @param {Function} fn
25212      * @param {Object} scope (optional)
25213      */
25214     sort : function(fn, scope){
25215         var cs = this.childNodes;
25216         var len = cs.length;
25217         if(len > 0){
25218             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25219             cs.sort(sortFn);
25220             for(var i = 0; i < len; i++){
25221                 var n = cs[i];
25222                 n.previousSibling = cs[i-1];
25223                 n.nextSibling = cs[i+1];
25224                 if(i == 0){
25225                     this.setFirstChild(n);
25226                 }
25227                 if(i == len-1){
25228                     this.setLastChild(n);
25229                 }
25230             }
25231         }
25232     },
25233
25234     /**
25235      * Returns true if this node is an ancestor (at any point) of the passed node.
25236      * @param {Node} node
25237      * @return {Boolean}
25238      */
25239     contains : function(node){
25240         return node.isAncestor(this);
25241     },
25242
25243     /**
25244      * Returns true if the passed node is an ancestor (at any point) of this node.
25245      * @param {Node} node
25246      * @return {Boolean}
25247      */
25248     isAncestor : function(node){
25249         var p = this.parentNode;
25250         while(p){
25251             if(p == node){
25252                 return true;
25253             }
25254             p = p.parentNode;
25255         }
25256         return false;
25257     },
25258
25259     toString : function(){
25260         return "[Node"+(this.id?" "+this.id:"")+"]";
25261     }
25262 });/*
25263  * Based on:
25264  * Ext JS Library 1.1.1
25265  * Copyright(c) 2006-2007, Ext JS, LLC.
25266  *
25267  * Originally Released Under LGPL - original licence link has changed is not relivant.
25268  *
25269  * Fork - LGPL
25270  * <script type="text/javascript">
25271  */
25272  (function(){ 
25273 /**
25274  * @class Roo.Layer
25275  * @extends Roo.Element
25276  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
25277  * automatic maintaining of shadow/shim positions.
25278  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
25279  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
25280  * you can pass a string with a CSS class name. False turns off the shadow.
25281  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
25282  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
25283  * @cfg {String} cls CSS class to add to the element
25284  * @cfg {Number} zindex Starting z-index (defaults to 11000)
25285  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
25286  * @constructor
25287  * @param {Object} config An object with config options.
25288  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
25289  */
25290
25291 Roo.Layer = function(config, existingEl){
25292     config = config || {};
25293     var dh = Roo.DomHelper;
25294     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
25295     if(existingEl){
25296         this.dom = Roo.getDom(existingEl);
25297     }
25298     if(!this.dom){
25299         var o = config.dh || {tag: "div", cls: "x-layer"};
25300         this.dom = dh.append(pel, o);
25301     }
25302     if(config.cls){
25303         this.addClass(config.cls);
25304     }
25305     this.constrain = config.constrain !== false;
25306     this.visibilityMode = Roo.Element.VISIBILITY;
25307     if(config.id){
25308         this.id = this.dom.id = config.id;
25309     }else{
25310         this.id = Roo.id(this.dom);
25311     }
25312     this.zindex = config.zindex || this.getZIndex();
25313     this.position("absolute", this.zindex);
25314     if(config.shadow){
25315         this.shadowOffset = config.shadowOffset || 4;
25316         this.shadow = new Roo.Shadow({
25317             offset : this.shadowOffset,
25318             mode : config.shadow
25319         });
25320     }else{
25321         this.shadowOffset = 0;
25322     }
25323     this.useShim = config.shim !== false && Roo.useShims;
25324     this.useDisplay = config.useDisplay;
25325     this.hide();
25326 };
25327
25328 var supr = Roo.Element.prototype;
25329
25330 // shims are shared among layer to keep from having 100 iframes
25331 var shims = [];
25332
25333 Roo.extend(Roo.Layer, Roo.Element, {
25334
25335     getZIndex : function(){
25336         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
25337     },
25338
25339     getShim : function(){
25340         if(!this.useShim){
25341             return null;
25342         }
25343         if(this.shim){
25344             return this.shim;
25345         }
25346         var shim = shims.shift();
25347         if(!shim){
25348             shim = this.createShim();
25349             shim.enableDisplayMode('block');
25350             shim.dom.style.display = 'none';
25351             shim.dom.style.visibility = 'visible';
25352         }
25353         var pn = this.dom.parentNode;
25354         if(shim.dom.parentNode != pn){
25355             pn.insertBefore(shim.dom, this.dom);
25356         }
25357         shim.setStyle('z-index', this.getZIndex()-2);
25358         this.shim = shim;
25359         return shim;
25360     },
25361
25362     hideShim : function(){
25363         if(this.shim){
25364             this.shim.setDisplayed(false);
25365             shims.push(this.shim);
25366             delete this.shim;
25367         }
25368     },
25369
25370     disableShadow : function(){
25371         if(this.shadow){
25372             this.shadowDisabled = true;
25373             this.shadow.hide();
25374             this.lastShadowOffset = this.shadowOffset;
25375             this.shadowOffset = 0;
25376         }
25377     },
25378
25379     enableShadow : function(show){
25380         if(this.shadow){
25381             this.shadowDisabled = false;
25382             this.shadowOffset = this.lastShadowOffset;
25383             delete this.lastShadowOffset;
25384             if(show){
25385                 this.sync(true);
25386             }
25387         }
25388     },
25389
25390     // private
25391     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
25392     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
25393     sync : function(doShow){
25394         var sw = this.shadow;
25395         if(!this.updating && this.isVisible() && (sw || this.useShim)){
25396             var sh = this.getShim();
25397
25398             var w = this.getWidth(),
25399                 h = this.getHeight();
25400
25401             var l = this.getLeft(true),
25402                 t = this.getTop(true);
25403
25404             if(sw && !this.shadowDisabled){
25405                 if(doShow && !sw.isVisible()){
25406                     sw.show(this);
25407                 }else{
25408                     sw.realign(l, t, w, h);
25409                 }
25410                 if(sh){
25411                     if(doShow){
25412                        sh.show();
25413                     }
25414                     // fit the shim behind the shadow, so it is shimmed too
25415                     var a = sw.adjusts, s = sh.dom.style;
25416                     s.left = (Math.min(l, l+a.l))+"px";
25417                     s.top = (Math.min(t, t+a.t))+"px";
25418                     s.width = (w+a.w)+"px";
25419                     s.height = (h+a.h)+"px";
25420                 }
25421             }else if(sh){
25422                 if(doShow){
25423                    sh.show();
25424                 }
25425                 sh.setSize(w, h);
25426                 sh.setLeftTop(l, t);
25427             }
25428             
25429         }
25430     },
25431
25432     // private
25433     destroy : function(){
25434         this.hideShim();
25435         if(this.shadow){
25436             this.shadow.hide();
25437         }
25438         this.removeAllListeners();
25439         var pn = this.dom.parentNode;
25440         if(pn){
25441             pn.removeChild(this.dom);
25442         }
25443         Roo.Element.uncache(this.id);
25444     },
25445
25446     remove : function(){
25447         this.destroy();
25448     },
25449
25450     // private
25451     beginUpdate : function(){
25452         this.updating = true;
25453     },
25454
25455     // private
25456     endUpdate : function(){
25457         this.updating = false;
25458         this.sync(true);
25459     },
25460
25461     // private
25462     hideUnders : function(negOffset){
25463         if(this.shadow){
25464             this.shadow.hide();
25465         }
25466         this.hideShim();
25467     },
25468
25469     // private
25470     constrainXY : function(){
25471         if(this.constrain){
25472             var vw = Roo.lib.Dom.getViewWidth(),
25473                 vh = Roo.lib.Dom.getViewHeight();
25474             var s = Roo.get(document).getScroll();
25475
25476             var xy = this.getXY();
25477             var x = xy[0], y = xy[1];   
25478             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
25479             // only move it if it needs it
25480             var moved = false;
25481             // first validate right/bottom
25482             if((x + w) > vw+s.left){
25483                 x = vw - w - this.shadowOffset;
25484                 moved = true;
25485             }
25486             if((y + h) > vh+s.top){
25487                 y = vh - h - this.shadowOffset;
25488                 moved = true;
25489             }
25490             // then make sure top/left isn't negative
25491             if(x < s.left){
25492                 x = s.left;
25493                 moved = true;
25494             }
25495             if(y < s.top){
25496                 y = s.top;
25497                 moved = true;
25498             }
25499             if(moved){
25500                 if(this.avoidY){
25501                     var ay = this.avoidY;
25502                     if(y <= ay && (y+h) >= ay){
25503                         y = ay-h-5;   
25504                     }
25505                 }
25506                 xy = [x, y];
25507                 this.storeXY(xy);
25508                 supr.setXY.call(this, xy);
25509                 this.sync();
25510             }
25511         }
25512     },
25513
25514     isVisible : function(){
25515         return this.visible;    
25516     },
25517
25518     // private
25519     showAction : function(){
25520         this.visible = true; // track visibility to prevent getStyle calls
25521         if(this.useDisplay === true){
25522             this.setDisplayed("");
25523         }else if(this.lastXY){
25524             supr.setXY.call(this, this.lastXY);
25525         }else if(this.lastLT){
25526             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
25527         }
25528     },
25529
25530     // private
25531     hideAction : function(){
25532         this.visible = false;
25533         if(this.useDisplay === true){
25534             this.setDisplayed(false);
25535         }else{
25536             this.setLeftTop(-10000,-10000);
25537         }
25538     },
25539
25540     // overridden Element method
25541     setVisible : function(v, a, d, c, e){
25542         if(v){
25543             this.showAction();
25544         }
25545         if(a && v){
25546             var cb = function(){
25547                 this.sync(true);
25548                 if(c){
25549                     c();
25550                 }
25551             }.createDelegate(this);
25552             supr.setVisible.call(this, true, true, d, cb, e);
25553         }else{
25554             if(!v){
25555                 this.hideUnders(true);
25556             }
25557             var cb = c;
25558             if(a){
25559                 cb = function(){
25560                     this.hideAction();
25561                     if(c){
25562                         c();
25563                     }
25564                 }.createDelegate(this);
25565             }
25566             supr.setVisible.call(this, v, a, d, cb, e);
25567             if(v){
25568                 this.sync(true);
25569             }else if(!a){
25570                 this.hideAction();
25571             }
25572         }
25573     },
25574
25575     storeXY : function(xy){
25576         delete this.lastLT;
25577         this.lastXY = xy;
25578     },
25579
25580     storeLeftTop : function(left, top){
25581         delete this.lastXY;
25582         this.lastLT = [left, top];
25583     },
25584
25585     // private
25586     beforeFx : function(){
25587         this.beforeAction();
25588         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
25589     },
25590
25591     // private
25592     afterFx : function(){
25593         Roo.Layer.superclass.afterFx.apply(this, arguments);
25594         this.sync(this.isVisible());
25595     },
25596
25597     // private
25598     beforeAction : function(){
25599         if(!this.updating && this.shadow){
25600             this.shadow.hide();
25601         }
25602     },
25603
25604     // overridden Element method
25605     setLeft : function(left){
25606         this.storeLeftTop(left, this.getTop(true));
25607         supr.setLeft.apply(this, arguments);
25608         this.sync();
25609     },
25610
25611     setTop : function(top){
25612         this.storeLeftTop(this.getLeft(true), top);
25613         supr.setTop.apply(this, arguments);
25614         this.sync();
25615     },
25616
25617     setLeftTop : function(left, top){
25618         this.storeLeftTop(left, top);
25619         supr.setLeftTop.apply(this, arguments);
25620         this.sync();
25621     },
25622
25623     setXY : function(xy, a, d, c, e){
25624         this.fixDisplay();
25625         this.beforeAction();
25626         this.storeXY(xy);
25627         var cb = this.createCB(c);
25628         supr.setXY.call(this, xy, a, d, cb, e);
25629         if(!a){
25630             cb();
25631         }
25632     },
25633
25634     // private
25635     createCB : function(c){
25636         var el = this;
25637         return function(){
25638             el.constrainXY();
25639             el.sync(true);
25640             if(c){
25641                 c();
25642             }
25643         };
25644     },
25645
25646     // overridden Element method
25647     setX : function(x, a, d, c, e){
25648         this.setXY([x, this.getY()], a, d, c, e);
25649     },
25650
25651     // overridden Element method
25652     setY : function(y, a, d, c, e){
25653         this.setXY([this.getX(), y], a, d, c, e);
25654     },
25655
25656     // overridden Element method
25657     setSize : function(w, h, a, d, c, e){
25658         this.beforeAction();
25659         var cb = this.createCB(c);
25660         supr.setSize.call(this, w, h, a, d, cb, e);
25661         if(!a){
25662             cb();
25663         }
25664     },
25665
25666     // overridden Element method
25667     setWidth : function(w, a, d, c, e){
25668         this.beforeAction();
25669         var cb = this.createCB(c);
25670         supr.setWidth.call(this, w, a, d, cb, e);
25671         if(!a){
25672             cb();
25673         }
25674     },
25675
25676     // overridden Element method
25677     setHeight : function(h, a, d, c, e){
25678         this.beforeAction();
25679         var cb = this.createCB(c);
25680         supr.setHeight.call(this, h, a, d, cb, e);
25681         if(!a){
25682             cb();
25683         }
25684     },
25685
25686     // overridden Element method
25687     setBounds : function(x, y, w, h, a, d, c, e){
25688         this.beforeAction();
25689         var cb = this.createCB(c);
25690         if(!a){
25691             this.storeXY([x, y]);
25692             supr.setXY.call(this, [x, y]);
25693             supr.setSize.call(this, w, h, a, d, cb, e);
25694             cb();
25695         }else{
25696             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
25697         }
25698         return this;
25699     },
25700     
25701     /**
25702      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
25703      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
25704      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
25705      * @param {Number} zindex The new z-index to set
25706      * @return {this} The Layer
25707      */
25708     setZIndex : function(zindex){
25709         this.zindex = zindex;
25710         this.setStyle("z-index", zindex + 2);
25711         if(this.shadow){
25712             this.shadow.setZIndex(zindex + 1);
25713         }
25714         if(this.shim){
25715             this.shim.setStyle("z-index", zindex);
25716         }
25717     }
25718 });
25719 })();/*
25720  * Based on:
25721  * Ext JS Library 1.1.1
25722  * Copyright(c) 2006-2007, Ext JS, LLC.
25723  *
25724  * Originally Released Under LGPL - original licence link has changed is not relivant.
25725  *
25726  * Fork - LGPL
25727  * <script type="text/javascript">
25728  */
25729
25730
25731 /**
25732  * @class Roo.Shadow
25733  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25734  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25735  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25736  * @constructor
25737  * Create a new Shadow
25738  * @param {Object} config The config object
25739  */
25740 Roo.Shadow = function(config){
25741     Roo.apply(this, config);
25742     if(typeof this.mode != "string"){
25743         this.mode = this.defaultMode;
25744     }
25745     var o = this.offset, a = {h: 0};
25746     var rad = Math.floor(this.offset/2);
25747     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
25748         case "drop":
25749             a.w = 0;
25750             a.l = a.t = o;
25751             a.t -= 1;
25752             if(Roo.isIE){
25753                 a.l -= this.offset + rad;
25754                 a.t -= this.offset + rad;
25755                 a.w -= rad;
25756                 a.h -= rad;
25757                 a.t += 1;
25758             }
25759         break;
25760         case "sides":
25761             a.w = (o*2);
25762             a.l = -o;
25763             a.t = o-1;
25764             if(Roo.isIE){
25765                 a.l -= (this.offset - rad);
25766                 a.t -= this.offset + rad;
25767                 a.l += 1;
25768                 a.w -= (this.offset - rad)*2;
25769                 a.w -= rad + 1;
25770                 a.h -= 1;
25771             }
25772         break;
25773         case "frame":
25774             a.w = a.h = (o*2);
25775             a.l = a.t = -o;
25776             a.t += 1;
25777             a.h -= 2;
25778             if(Roo.isIE){
25779                 a.l -= (this.offset - rad);
25780                 a.t -= (this.offset - rad);
25781                 a.l += 1;
25782                 a.w -= (this.offset + rad + 1);
25783                 a.h -= (this.offset + rad);
25784                 a.h += 1;
25785             }
25786         break;
25787     };
25788
25789     this.adjusts = a;
25790 };
25791
25792 Roo.Shadow.prototype = {
25793     /**
25794      * @cfg {String} mode
25795      * The shadow display mode.  Supports the following options:<br />
25796      * sides: Shadow displays on both sides and bottom only<br />
25797      * frame: Shadow displays equally on all four sides<br />
25798      * drop: Traditional bottom-right drop shadow (default)
25799      */
25800     /**
25801      * @cfg {String} offset
25802      * The number of pixels to offset the shadow from the element (defaults to 4)
25803      */
25804     offset: 4,
25805
25806     // private
25807     defaultMode: "drop",
25808
25809     /**
25810      * Displays the shadow under the target element
25811      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
25812      */
25813     show : function(target){
25814         target = Roo.get(target);
25815         if(!this.el){
25816             this.el = Roo.Shadow.Pool.pull();
25817             if(this.el.dom.nextSibling != target.dom){
25818                 this.el.insertBefore(target);
25819             }
25820         }
25821         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
25822         if(Roo.isIE){
25823             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
25824         }
25825         this.realign(
25826             target.getLeft(true),
25827             target.getTop(true),
25828             target.getWidth(),
25829             target.getHeight()
25830         );
25831         this.el.dom.style.display = "block";
25832     },
25833
25834     /**
25835      * Returns true if the shadow is visible, else false
25836      */
25837     isVisible : function(){
25838         return this.el ? true : false;  
25839     },
25840
25841     /**
25842      * Direct alignment when values are already available. Show must be called at least once before
25843      * calling this method to ensure it is initialized.
25844      * @param {Number} left The target element left position
25845      * @param {Number} top The target element top position
25846      * @param {Number} width The target element width
25847      * @param {Number} height The target element height
25848      */
25849     realign : function(l, t, w, h){
25850         if(!this.el){
25851             return;
25852         }
25853         var a = this.adjusts, d = this.el.dom, s = d.style;
25854         var iea = 0;
25855         s.left = (l+a.l)+"px";
25856         s.top = (t+a.t)+"px";
25857         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
25858  
25859         if(s.width != sws || s.height != shs){
25860             s.width = sws;
25861             s.height = shs;
25862             if(!Roo.isIE){
25863                 var cn = d.childNodes;
25864                 var sww = Math.max(0, (sw-12))+"px";
25865                 cn[0].childNodes[1].style.width = sww;
25866                 cn[1].childNodes[1].style.width = sww;
25867                 cn[2].childNodes[1].style.width = sww;
25868                 cn[1].style.height = Math.max(0, (sh-12))+"px";
25869             }
25870         }
25871     },
25872
25873     /**
25874      * Hides this shadow
25875      */
25876     hide : function(){
25877         if(this.el){
25878             this.el.dom.style.display = "none";
25879             Roo.Shadow.Pool.push(this.el);
25880             delete this.el;
25881         }
25882     },
25883
25884     /**
25885      * Adjust the z-index of this shadow
25886      * @param {Number} zindex The new z-index
25887      */
25888     setZIndex : function(z){
25889         this.zIndex = z;
25890         if(this.el){
25891             this.el.setStyle("z-index", z);
25892         }
25893     }
25894 };
25895
25896 // Private utility class that manages the internal Shadow cache
25897 Roo.Shadow.Pool = function(){
25898     var p = [];
25899     var markup = Roo.isIE ?
25900                  '<div class="x-ie-shadow"></div>' :
25901                  '<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>';
25902     return {
25903         pull : function(){
25904             var sh = p.shift();
25905             if(!sh){
25906                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
25907                 sh.autoBoxAdjust = false;
25908             }
25909             return sh;
25910         },
25911
25912         push : function(sh){
25913             p.push(sh);
25914         }
25915     };
25916 }();/*
25917  * Based on:
25918  * Ext JS Library 1.1.1
25919  * Copyright(c) 2006-2007, Ext JS, LLC.
25920  *
25921  * Originally Released Under LGPL - original licence link has changed is not relivant.
25922  *
25923  * Fork - LGPL
25924  * <script type="text/javascript">
25925  */
25926
25927
25928 /**
25929  * @class Roo.SplitBar
25930  * @extends Roo.util.Observable
25931  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
25932  * <br><br>
25933  * Usage:
25934  * <pre><code>
25935 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
25936                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
25937 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
25938 split.minSize = 100;
25939 split.maxSize = 600;
25940 split.animate = true;
25941 split.on('moved', splitterMoved);
25942 </code></pre>
25943  * @constructor
25944  * Create a new SplitBar
25945  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
25946  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
25947  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
25948  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
25949                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
25950                         position of the SplitBar).
25951  */
25952 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
25953     
25954     /** @private */
25955     this.el = Roo.get(dragElement, true);
25956     this.el.dom.unselectable = "on";
25957     /** @private */
25958     this.resizingEl = Roo.get(resizingElement, true);
25959
25960     /**
25961      * @private
25962      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
25963      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
25964      * @type Number
25965      */
25966     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
25967     
25968     /**
25969      * The minimum size of the resizing element. (Defaults to 0)
25970      * @type Number
25971      */
25972     this.minSize = 0;
25973     
25974     /**
25975      * The maximum size of the resizing element. (Defaults to 2000)
25976      * @type Number
25977      */
25978     this.maxSize = 2000;
25979     
25980     /**
25981      * Whether to animate the transition to the new size
25982      * @type Boolean
25983      */
25984     this.animate = false;
25985     
25986     /**
25987      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
25988      * @type Boolean
25989      */
25990     this.useShim = false;
25991     
25992     /** @private */
25993     this.shim = null;
25994     
25995     if(!existingProxy){
25996         /** @private */
25997         this.proxy = Roo.SplitBar.createProxy(this.orientation);
25998     }else{
25999         this.proxy = Roo.get(existingProxy).dom;
26000     }
26001     /** @private */
26002     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26003     
26004     /** @private */
26005     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26006     
26007     /** @private */
26008     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26009     
26010     /** @private */
26011     this.dragSpecs = {};
26012     
26013     /**
26014      * @private The adapter to use to positon and resize elements
26015      */
26016     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26017     this.adapter.init(this);
26018     
26019     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26020         /** @private */
26021         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26022         this.el.addClass("x-splitbar-h");
26023     }else{
26024         /** @private */
26025         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26026         this.el.addClass("x-splitbar-v");
26027     }
26028     
26029     this.addEvents({
26030         /**
26031          * @event resize
26032          * Fires when the splitter is moved (alias for {@link #event-moved})
26033          * @param {Roo.SplitBar} this
26034          * @param {Number} newSize the new width or height
26035          */
26036         "resize" : true,
26037         /**
26038          * @event moved
26039          * Fires when the splitter is moved
26040          * @param {Roo.SplitBar} this
26041          * @param {Number} newSize the new width or height
26042          */
26043         "moved" : true,
26044         /**
26045          * @event beforeresize
26046          * Fires before the splitter is dragged
26047          * @param {Roo.SplitBar} this
26048          */
26049         "beforeresize" : true,
26050
26051         "beforeapply" : true
26052     });
26053
26054     Roo.util.Observable.call(this);
26055 };
26056
26057 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26058     onStartProxyDrag : function(x, y){
26059         this.fireEvent("beforeresize", this);
26060         if(!this.overlay){
26061             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26062             o.unselectable();
26063             o.enableDisplayMode("block");
26064             // all splitbars share the same overlay
26065             Roo.SplitBar.prototype.overlay = o;
26066         }
26067         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26068         this.overlay.show();
26069         Roo.get(this.proxy).setDisplayed("block");
26070         var size = this.adapter.getElementSize(this);
26071         this.activeMinSize = this.getMinimumSize();;
26072         this.activeMaxSize = this.getMaximumSize();;
26073         var c1 = size - this.activeMinSize;
26074         var c2 = Math.max(this.activeMaxSize - size, 0);
26075         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26076             this.dd.resetConstraints();
26077             this.dd.setXConstraint(
26078                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26079                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26080             );
26081             this.dd.setYConstraint(0, 0);
26082         }else{
26083             this.dd.resetConstraints();
26084             this.dd.setXConstraint(0, 0);
26085             this.dd.setYConstraint(
26086                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26087                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26088             );
26089          }
26090         this.dragSpecs.startSize = size;
26091         this.dragSpecs.startPoint = [x, y];
26092         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26093     },
26094     
26095     /** 
26096      * @private Called after the drag operation by the DDProxy
26097      */
26098     onEndProxyDrag : function(e){
26099         Roo.get(this.proxy).setDisplayed(false);
26100         var endPoint = Roo.lib.Event.getXY(e);
26101         if(this.overlay){
26102             this.overlay.hide();
26103         }
26104         var newSize;
26105         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26106             newSize = this.dragSpecs.startSize + 
26107                 (this.placement == Roo.SplitBar.LEFT ?
26108                     endPoint[0] - this.dragSpecs.startPoint[0] :
26109                     this.dragSpecs.startPoint[0] - endPoint[0]
26110                 );
26111         }else{
26112             newSize = this.dragSpecs.startSize + 
26113                 (this.placement == Roo.SplitBar.TOP ?
26114                     endPoint[1] - this.dragSpecs.startPoint[1] :
26115                     this.dragSpecs.startPoint[1] - endPoint[1]
26116                 );
26117         }
26118         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26119         if(newSize != this.dragSpecs.startSize){
26120             if(this.fireEvent('beforeapply', this, newSize) !== false){
26121                 this.adapter.setElementSize(this, newSize);
26122                 this.fireEvent("moved", this, newSize);
26123                 this.fireEvent("resize", this, newSize);
26124             }
26125         }
26126     },
26127     
26128     /**
26129      * Get the adapter this SplitBar uses
26130      * @return The adapter object
26131      */
26132     getAdapter : function(){
26133         return this.adapter;
26134     },
26135     
26136     /**
26137      * Set the adapter this SplitBar uses
26138      * @param {Object} adapter A SplitBar adapter object
26139      */
26140     setAdapter : function(adapter){
26141         this.adapter = adapter;
26142         this.adapter.init(this);
26143     },
26144     
26145     /**
26146      * Gets the minimum size for the resizing element
26147      * @return {Number} The minimum size
26148      */
26149     getMinimumSize : function(){
26150         return this.minSize;
26151     },
26152     
26153     /**
26154      * Sets the minimum size for the resizing element
26155      * @param {Number} minSize The minimum size
26156      */
26157     setMinimumSize : function(minSize){
26158         this.minSize = minSize;
26159     },
26160     
26161     /**
26162      * Gets the maximum size for the resizing element
26163      * @return {Number} The maximum size
26164      */
26165     getMaximumSize : function(){
26166         return this.maxSize;
26167     },
26168     
26169     /**
26170      * Sets the maximum size for the resizing element
26171      * @param {Number} maxSize The maximum size
26172      */
26173     setMaximumSize : function(maxSize){
26174         this.maxSize = maxSize;
26175     },
26176     
26177     /**
26178      * Sets the initialize size for the resizing element
26179      * @param {Number} size The initial size
26180      */
26181     setCurrentSize : function(size){
26182         var oldAnimate = this.animate;
26183         this.animate = false;
26184         this.adapter.setElementSize(this, size);
26185         this.animate = oldAnimate;
26186     },
26187     
26188     /**
26189      * Destroy this splitbar. 
26190      * @param {Boolean} removeEl True to remove the element
26191      */
26192     destroy : function(removeEl){
26193         if(this.shim){
26194             this.shim.remove();
26195         }
26196         this.dd.unreg();
26197         this.proxy.parentNode.removeChild(this.proxy);
26198         if(removeEl){
26199             this.el.remove();
26200         }
26201     }
26202 });
26203
26204 /**
26205  * @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.
26206  */
26207 Roo.SplitBar.createProxy = function(dir){
26208     var proxy = new Roo.Element(document.createElement("div"));
26209     proxy.unselectable();
26210     var cls = 'x-splitbar-proxy';
26211     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26212     document.body.appendChild(proxy.dom);
26213     return proxy.dom;
26214 };
26215
26216 /** 
26217  * @class Roo.SplitBar.BasicLayoutAdapter
26218  * Default Adapter. It assumes the splitter and resizing element are not positioned
26219  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26220  */
26221 Roo.SplitBar.BasicLayoutAdapter = function(){
26222 };
26223
26224 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26225     // do nothing for now
26226     init : function(s){
26227     
26228     },
26229     /**
26230      * Called before drag operations to get the current size of the resizing element. 
26231      * @param {Roo.SplitBar} s The SplitBar using this adapter
26232      */
26233      getElementSize : function(s){
26234         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26235             return s.resizingEl.getWidth();
26236         }else{
26237             return s.resizingEl.getHeight();
26238         }
26239     },
26240     
26241     /**
26242      * Called after drag operations to set the size of the resizing element.
26243      * @param {Roo.SplitBar} s The SplitBar using this adapter
26244      * @param {Number} newSize The new size to set
26245      * @param {Function} onComplete A function to be invoked when resizing is complete
26246      */
26247     setElementSize : function(s, newSize, onComplete){
26248         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26249             if(!s.animate){
26250                 s.resizingEl.setWidth(newSize);
26251                 if(onComplete){
26252                     onComplete(s, newSize);
26253                 }
26254             }else{
26255                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26256             }
26257         }else{
26258             
26259             if(!s.animate){
26260                 s.resizingEl.setHeight(newSize);
26261                 if(onComplete){
26262                     onComplete(s, newSize);
26263                 }
26264             }else{
26265                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26266             }
26267         }
26268     }
26269 };
26270
26271 /** 
26272  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26273  * @extends Roo.SplitBar.BasicLayoutAdapter
26274  * Adapter that  moves the splitter element to align with the resized sizing element. 
26275  * Used with an absolute positioned SplitBar.
26276  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26277  * document.body, make sure you assign an id to the body element.
26278  */
26279 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26280     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26281     this.container = Roo.get(container);
26282 };
26283
26284 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26285     init : function(s){
26286         this.basic.init(s);
26287     },
26288     
26289     getElementSize : function(s){
26290         return this.basic.getElementSize(s);
26291     },
26292     
26293     setElementSize : function(s, newSize, onComplete){
26294         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26295     },
26296     
26297     moveSplitter : function(s){
26298         var yes = Roo.SplitBar;
26299         switch(s.placement){
26300             case yes.LEFT:
26301                 s.el.setX(s.resizingEl.getRight());
26302                 break;
26303             case yes.RIGHT:
26304                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26305                 break;
26306             case yes.TOP:
26307                 s.el.setY(s.resizingEl.getBottom());
26308                 break;
26309             case yes.BOTTOM:
26310                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26311                 break;
26312         }
26313     }
26314 };
26315
26316 /**
26317  * Orientation constant - Create a vertical SplitBar
26318  * @static
26319  * @type Number
26320  */
26321 Roo.SplitBar.VERTICAL = 1;
26322
26323 /**
26324  * Orientation constant - Create a horizontal SplitBar
26325  * @static
26326  * @type Number
26327  */
26328 Roo.SplitBar.HORIZONTAL = 2;
26329
26330 /**
26331  * Placement constant - The resizing element is to the left of the splitter element
26332  * @static
26333  * @type Number
26334  */
26335 Roo.SplitBar.LEFT = 1;
26336
26337 /**
26338  * Placement constant - The resizing element is to the right of the splitter element
26339  * @static
26340  * @type Number
26341  */
26342 Roo.SplitBar.RIGHT = 2;
26343
26344 /**
26345  * Placement constant - The resizing element is positioned above the splitter element
26346  * @static
26347  * @type Number
26348  */
26349 Roo.SplitBar.TOP = 3;
26350
26351 /**
26352  * Placement constant - The resizing element is positioned under splitter element
26353  * @static
26354  * @type Number
26355  */
26356 Roo.SplitBar.BOTTOM = 4;
26357 /*
26358  * Based on:
26359  * Ext JS Library 1.1.1
26360  * Copyright(c) 2006-2007, Ext JS, LLC.
26361  *
26362  * Originally Released Under LGPL - original licence link has changed is not relivant.
26363  *
26364  * Fork - LGPL
26365  * <script type="text/javascript">
26366  */
26367
26368 /**
26369  * @class Roo.View
26370  * @extends Roo.util.Observable
26371  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26372  * This class also supports single and multi selection modes. <br>
26373  * Create a data model bound view:
26374  <pre><code>
26375  var store = new Roo.data.Store(...);
26376
26377  var view = new Roo.View({
26378     el : "my-element",
26379     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26380  
26381     singleSelect: true,
26382     selectedClass: "ydataview-selected",
26383     store: store
26384  });
26385
26386  // listen for node click?
26387  view.on("click", function(vw, index, node, e){
26388  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26389  });
26390
26391  // load XML data
26392  dataModel.load("foobar.xml");
26393  </code></pre>
26394  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26395  * <br><br>
26396  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26397  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26398  * 
26399  * Note: old style constructor is still suported (container, template, config)
26400  * 
26401  * @constructor
26402  * Create a new View
26403  * @param {Object} config The config object
26404  * 
26405  */
26406 Roo.View = function(config, depreciated_tpl, depreciated_config){
26407     
26408     this.parent = false;
26409     
26410     if (typeof(depreciated_tpl) == 'undefined') {
26411         // new way.. - universal constructor.
26412         Roo.apply(this, config);
26413         this.el  = Roo.get(this.el);
26414     } else {
26415         // old format..
26416         this.el  = Roo.get(config);
26417         this.tpl = depreciated_tpl;
26418         Roo.apply(this, depreciated_config);
26419     }
26420     this.wrapEl  = this.el.wrap().wrap();
26421     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26422     
26423     
26424     if(typeof(this.tpl) == "string"){
26425         this.tpl = new Roo.Template(this.tpl);
26426     } else {
26427         // support xtype ctors..
26428         this.tpl = new Roo.factory(this.tpl, Roo);
26429     }
26430     
26431     
26432     this.tpl.compile();
26433     
26434     /** @private */
26435     this.addEvents({
26436         /**
26437          * @event beforeclick
26438          * Fires before a click is processed. Returns false to cancel the default action.
26439          * @param {Roo.View} this
26440          * @param {Number} index The index of the target node
26441          * @param {HTMLElement} node The target node
26442          * @param {Roo.EventObject} e The raw event object
26443          */
26444             "beforeclick" : true,
26445         /**
26446          * @event click
26447          * Fires when a template node is clicked.
26448          * @param {Roo.View} this
26449          * @param {Number} index The index of the target node
26450          * @param {HTMLElement} node The target node
26451          * @param {Roo.EventObject} e The raw event object
26452          */
26453             "click" : true,
26454         /**
26455          * @event dblclick
26456          * Fires when a template node is double clicked.
26457          * @param {Roo.View} this
26458          * @param {Number} index The index of the target node
26459          * @param {HTMLElement} node The target node
26460          * @param {Roo.EventObject} e The raw event object
26461          */
26462             "dblclick" : true,
26463         /**
26464          * @event contextmenu
26465          * Fires when a template node is right clicked.
26466          * @param {Roo.View} this
26467          * @param {Number} index The index of the target node
26468          * @param {HTMLElement} node The target node
26469          * @param {Roo.EventObject} e The raw event object
26470          */
26471             "contextmenu" : true,
26472         /**
26473          * @event selectionchange
26474          * Fires when the selected nodes change.
26475          * @param {Roo.View} this
26476          * @param {Array} selections Array of the selected nodes
26477          */
26478             "selectionchange" : true,
26479     
26480         /**
26481          * @event beforeselect
26482          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26483          * @param {Roo.View} this
26484          * @param {HTMLElement} node The node to be selected
26485          * @param {Array} selections Array of currently selected nodes
26486          */
26487             "beforeselect" : true,
26488         /**
26489          * @event preparedata
26490          * Fires on every row to render, to allow you to change the data.
26491          * @param {Roo.View} this
26492          * @param {Object} data to be rendered (change this)
26493          */
26494           "preparedata" : true
26495           
26496           
26497         });
26498
26499
26500
26501     this.el.on({
26502         "click": this.onClick,
26503         "dblclick": this.onDblClick,
26504         "contextmenu": this.onContextMenu,
26505         scope:this
26506     });
26507
26508     this.selections = [];
26509     this.nodes = [];
26510     this.cmp = new Roo.CompositeElementLite([]);
26511     if(this.store){
26512         this.store = Roo.factory(this.store, Roo.data);
26513         this.setStore(this.store, true);
26514     }
26515     
26516     if ( this.footer && this.footer.xtype) {
26517            
26518          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26519         
26520         this.footer.dataSource = this.store;
26521         this.footer.container = fctr;
26522         this.footer = Roo.factory(this.footer, Roo);
26523         fctr.insertFirst(this.el);
26524         
26525         // this is a bit insane - as the paging toolbar seems to detach the el..
26526 //        dom.parentNode.parentNode.parentNode
26527          // they get detached?
26528     }
26529     
26530     
26531     Roo.View.superclass.constructor.call(this);
26532     
26533     
26534 };
26535
26536 Roo.extend(Roo.View, Roo.util.Observable, {
26537     
26538      /**
26539      * @cfg {Roo.data.Store} store Data store to load data from.
26540      */
26541     store : false,
26542     
26543     /**
26544      * @cfg {String|Roo.Element} el The container element.
26545      */
26546     el : '',
26547     
26548     /**
26549      * @cfg {String|Roo.Template} tpl The template used by this View 
26550      */
26551     tpl : false,
26552     /**
26553      * @cfg {String} dataName the named area of the template to use as the data area
26554      *                          Works with domtemplates roo-name="name"
26555      */
26556     dataName: false,
26557     /**
26558      * @cfg {String} selectedClass The css class to add to selected nodes
26559      */
26560     selectedClass : "x-view-selected",
26561      /**
26562      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26563      */
26564     emptyText : "",
26565     
26566     /**
26567      * @cfg {String} text to display on mask (default Loading)
26568      */
26569     mask : false,
26570     /**
26571      * @cfg {Boolean} multiSelect Allow multiple selection
26572      */
26573     multiSelect : false,
26574     /**
26575      * @cfg {Boolean} singleSelect Allow single selection
26576      */
26577     singleSelect:  false,
26578     
26579     /**
26580      * @cfg {Boolean} toggleSelect - selecting 
26581      */
26582     toggleSelect : false,
26583     
26584     /**
26585      * @cfg {Boolean} tickable - selecting 
26586      */
26587     tickable : false,
26588     
26589     /**
26590      * Returns the element this view is bound to.
26591      * @return {Roo.Element}
26592      */
26593     getEl : function(){
26594         return this.wrapEl;
26595     },
26596     
26597     
26598
26599     /**
26600      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26601      */
26602     refresh : function(){
26603         //Roo.log('refresh');
26604         var t = this.tpl;
26605         
26606         // if we are using something like 'domtemplate', then
26607         // the what gets used is:
26608         // t.applySubtemplate(NAME, data, wrapping data..)
26609         // the outer template then get' applied with
26610         //     the store 'extra data'
26611         // and the body get's added to the
26612         //      roo-name="data" node?
26613         //      <span class='roo-tpl-{name}'></span> ?????
26614         
26615         
26616         
26617         this.clearSelections();
26618         this.el.update("");
26619         var html = [];
26620         var records = this.store.getRange();
26621         if(records.length < 1) {
26622             
26623             // is this valid??  = should it render a template??
26624             
26625             this.el.update(this.emptyText);
26626             return;
26627         }
26628         var el = this.el;
26629         if (this.dataName) {
26630             this.el.update(t.apply(this.store.meta)); //????
26631             el = this.el.child('.roo-tpl-' + this.dataName);
26632         }
26633         
26634         for(var i = 0, len = records.length; i < len; i++){
26635             var data = this.prepareData(records[i].data, i, records[i]);
26636             this.fireEvent("preparedata", this, data, i, records[i]);
26637             
26638             var d = Roo.apply({}, data);
26639             
26640             if(this.tickable){
26641                 Roo.apply(d, {'roo-id' : Roo.id()});
26642                 
26643                 var _this = this;
26644             
26645                 Roo.each(this.parent.item, function(item){
26646                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26647                         return;
26648                     }
26649                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26650                 });
26651             }
26652             
26653             html[html.length] = Roo.util.Format.trim(
26654                 this.dataName ?
26655                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26656                     t.apply(d)
26657             );
26658         }
26659         
26660         
26661         
26662         el.update(html.join(""));
26663         this.nodes = el.dom.childNodes;
26664         this.updateIndexes(0);
26665     },
26666     
26667
26668     /**
26669      * Function to override to reformat the data that is sent to
26670      * the template for each node.
26671      * DEPRICATED - use the preparedata event handler.
26672      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26673      * a JSON object for an UpdateManager bound view).
26674      */
26675     prepareData : function(data, index, record)
26676     {
26677         this.fireEvent("preparedata", this, data, index, record);
26678         return data;
26679     },
26680
26681     onUpdate : function(ds, record){
26682         // Roo.log('on update');   
26683         this.clearSelections();
26684         var index = this.store.indexOf(record);
26685         var n = this.nodes[index];
26686         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26687         n.parentNode.removeChild(n);
26688         this.updateIndexes(index, index);
26689     },
26690
26691     
26692     
26693 // --------- FIXME     
26694     onAdd : function(ds, records, index)
26695     {
26696         //Roo.log(['on Add', ds, records, index] );        
26697         this.clearSelections();
26698         if(this.nodes.length == 0){
26699             this.refresh();
26700             return;
26701         }
26702         var n = this.nodes[index];
26703         for(var i = 0, len = records.length; i < len; i++){
26704             var d = this.prepareData(records[i].data, i, records[i]);
26705             if(n){
26706                 this.tpl.insertBefore(n, d);
26707             }else{
26708                 
26709                 this.tpl.append(this.el, d);
26710             }
26711         }
26712         this.updateIndexes(index);
26713     },
26714
26715     onRemove : function(ds, record, index){
26716        // Roo.log('onRemove');
26717         this.clearSelections();
26718         var el = this.dataName  ?
26719             this.el.child('.roo-tpl-' + this.dataName) :
26720             this.el; 
26721         
26722         el.dom.removeChild(this.nodes[index]);
26723         this.updateIndexes(index);
26724     },
26725
26726     /**
26727      * Refresh an individual node.
26728      * @param {Number} index
26729      */
26730     refreshNode : function(index){
26731         this.onUpdate(this.store, this.store.getAt(index));
26732     },
26733
26734     updateIndexes : function(startIndex, endIndex){
26735         var ns = this.nodes;
26736         startIndex = startIndex || 0;
26737         endIndex = endIndex || ns.length - 1;
26738         for(var i = startIndex; i <= endIndex; i++){
26739             ns[i].nodeIndex = i;
26740         }
26741     },
26742
26743     /**
26744      * Changes the data store this view uses and refresh the view.
26745      * @param {Store} store
26746      */
26747     setStore : function(store, initial){
26748         if(!initial && this.store){
26749             this.store.un("datachanged", this.refresh);
26750             this.store.un("add", this.onAdd);
26751             this.store.un("remove", this.onRemove);
26752             this.store.un("update", this.onUpdate);
26753             this.store.un("clear", this.refresh);
26754             this.store.un("beforeload", this.onBeforeLoad);
26755             this.store.un("load", this.onLoad);
26756             this.store.un("loadexception", this.onLoad);
26757         }
26758         if(store){
26759           
26760             store.on("datachanged", this.refresh, this);
26761             store.on("add", this.onAdd, this);
26762             store.on("remove", this.onRemove, this);
26763             store.on("update", this.onUpdate, this);
26764             store.on("clear", this.refresh, this);
26765             store.on("beforeload", this.onBeforeLoad, this);
26766             store.on("load", this.onLoad, this);
26767             store.on("loadexception", this.onLoad, this);
26768         }
26769         
26770         if(store){
26771             this.refresh();
26772         }
26773     },
26774     /**
26775      * onbeforeLoad - masks the loading area.
26776      *
26777      */
26778     onBeforeLoad : function(store,opts)
26779     {
26780          //Roo.log('onBeforeLoad');   
26781         if (!opts.add) {
26782             this.el.update("");
26783         }
26784         this.el.mask(this.mask ? this.mask : "Loading" ); 
26785     },
26786     onLoad : function ()
26787     {
26788         this.el.unmask();
26789     },
26790     
26791
26792     /**
26793      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
26794      * @param {HTMLElement} node
26795      * @return {HTMLElement} The template node
26796      */
26797     findItemFromChild : function(node){
26798         var el = this.dataName  ?
26799             this.el.child('.roo-tpl-' + this.dataName,true) :
26800             this.el.dom; 
26801         
26802         if(!node || node.parentNode == el){
26803                     return node;
26804             }
26805             var p = node.parentNode;
26806             while(p && p != el){
26807             if(p.parentNode == el){
26808                 return p;
26809             }
26810             p = p.parentNode;
26811         }
26812             return null;
26813     },
26814
26815     /** @ignore */
26816     onClick : function(e){
26817         var item = this.findItemFromChild(e.getTarget());
26818         if(item){
26819             var index = this.indexOf(item);
26820             if(this.onItemClick(item, index, e) !== false){
26821                 this.fireEvent("click", this, index, item, e);
26822             }
26823         }else{
26824             this.clearSelections();
26825         }
26826     },
26827
26828     /** @ignore */
26829     onContextMenu : function(e){
26830         var item = this.findItemFromChild(e.getTarget());
26831         if(item){
26832             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
26833         }
26834     },
26835
26836     /** @ignore */
26837     onDblClick : function(e){
26838         var item = this.findItemFromChild(e.getTarget());
26839         if(item){
26840             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
26841         }
26842     },
26843
26844     onItemClick : function(item, index, e)
26845     {
26846         if(this.fireEvent("beforeclick", this, index, item, e) === false){
26847             return false;
26848         }
26849         if (this.toggleSelect) {
26850             var m = this.isSelected(item) ? 'unselect' : 'select';
26851             //Roo.log(m);
26852             var _t = this;
26853             _t[m](item, true, false);
26854             return true;
26855         }
26856         if(this.multiSelect || this.singleSelect){
26857             if(this.multiSelect && e.shiftKey && this.lastSelection){
26858                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
26859             }else{
26860                 this.select(item, this.multiSelect && e.ctrlKey);
26861                 this.lastSelection = item;
26862             }
26863             
26864             if(!this.tickable){
26865                 e.preventDefault();
26866             }
26867             
26868         }
26869         return true;
26870     },
26871
26872     /**
26873      * Get the number of selected nodes.
26874      * @return {Number}
26875      */
26876     getSelectionCount : function(){
26877         return this.selections.length;
26878     },
26879
26880     /**
26881      * Get the currently selected nodes.
26882      * @return {Array} An array of HTMLElements
26883      */
26884     getSelectedNodes : function(){
26885         return this.selections;
26886     },
26887
26888     /**
26889      * Get the indexes of the selected nodes.
26890      * @return {Array}
26891      */
26892     getSelectedIndexes : function(){
26893         var indexes = [], s = this.selections;
26894         for(var i = 0, len = s.length; i < len; i++){
26895             indexes.push(s[i].nodeIndex);
26896         }
26897         return indexes;
26898     },
26899
26900     /**
26901      * Clear all selections
26902      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
26903      */
26904     clearSelections : function(suppressEvent){
26905         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
26906             this.cmp.elements = this.selections;
26907             this.cmp.removeClass(this.selectedClass);
26908             this.selections = [];
26909             if(!suppressEvent){
26910                 this.fireEvent("selectionchange", this, this.selections);
26911             }
26912         }
26913     },
26914
26915     /**
26916      * Returns true if the passed node is selected
26917      * @param {HTMLElement/Number} node The node or node index
26918      * @return {Boolean}
26919      */
26920     isSelected : function(node){
26921         var s = this.selections;
26922         if(s.length < 1){
26923             return false;
26924         }
26925         node = this.getNode(node);
26926         return s.indexOf(node) !== -1;
26927     },
26928
26929     /**
26930      * Selects nodes.
26931      * @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
26932      * @param {Boolean} keepExisting (optional) true to keep existing selections
26933      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
26934      */
26935     select : function(nodeInfo, keepExisting, suppressEvent){
26936         if(nodeInfo instanceof Array){
26937             if(!keepExisting){
26938                 this.clearSelections(true);
26939             }
26940             for(var i = 0, len = nodeInfo.length; i < len; i++){
26941                 this.select(nodeInfo[i], true, true);
26942             }
26943             return;
26944         } 
26945         var node = this.getNode(nodeInfo);
26946         if(!node || this.isSelected(node)){
26947             return; // already selected.
26948         }
26949         if(!keepExisting){
26950             this.clearSelections(true);
26951         }
26952         
26953         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
26954             Roo.fly(node).addClass(this.selectedClass);
26955             this.selections.push(node);
26956             if(!suppressEvent){
26957                 this.fireEvent("selectionchange", this, this.selections);
26958             }
26959         }
26960         
26961         
26962     },
26963       /**
26964      * Unselects nodes.
26965      * @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
26966      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
26967      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
26968      */
26969     unselect : function(nodeInfo, keepExisting, suppressEvent)
26970     {
26971         if(nodeInfo instanceof Array){
26972             Roo.each(this.selections, function(s) {
26973                 this.unselect(s, nodeInfo);
26974             }, this);
26975             return;
26976         }
26977         var node = this.getNode(nodeInfo);
26978         if(!node || !this.isSelected(node)){
26979             //Roo.log("not selected");
26980             return; // not selected.
26981         }
26982         // fireevent???
26983         var ns = [];
26984         Roo.each(this.selections, function(s) {
26985             if (s == node ) {
26986                 Roo.fly(node).removeClass(this.selectedClass);
26987
26988                 return;
26989             }
26990             ns.push(s);
26991         },this);
26992         
26993         this.selections= ns;
26994         this.fireEvent("selectionchange", this, this.selections);
26995     },
26996
26997     /**
26998      * Gets a template node.
26999      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27000      * @return {HTMLElement} The node or null if it wasn't found
27001      */
27002     getNode : function(nodeInfo){
27003         if(typeof nodeInfo == "string"){
27004             return document.getElementById(nodeInfo);
27005         }else if(typeof nodeInfo == "number"){
27006             return this.nodes[nodeInfo];
27007         }
27008         return nodeInfo;
27009     },
27010
27011     /**
27012      * Gets a range template nodes.
27013      * @param {Number} startIndex
27014      * @param {Number} endIndex
27015      * @return {Array} An array of nodes
27016      */
27017     getNodes : function(start, end){
27018         var ns = this.nodes;
27019         start = start || 0;
27020         end = typeof end == "undefined" ? ns.length - 1 : end;
27021         var nodes = [];
27022         if(start <= end){
27023             for(var i = start; i <= end; i++){
27024                 nodes.push(ns[i]);
27025             }
27026         } else{
27027             for(var i = start; i >= end; i--){
27028                 nodes.push(ns[i]);
27029             }
27030         }
27031         return nodes;
27032     },
27033
27034     /**
27035      * Finds the index of the passed node
27036      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27037      * @return {Number} The index of the node or -1
27038      */
27039     indexOf : function(node){
27040         node = this.getNode(node);
27041         if(typeof node.nodeIndex == "number"){
27042             return node.nodeIndex;
27043         }
27044         var ns = this.nodes;
27045         for(var i = 0, len = ns.length; i < len; i++){
27046             if(ns[i] == node){
27047                 return i;
27048             }
27049         }
27050         return -1;
27051     }
27052 });
27053 /*
27054  * Based on:
27055  * Ext JS Library 1.1.1
27056  * Copyright(c) 2006-2007, Ext JS, LLC.
27057  *
27058  * Originally Released Under LGPL - original licence link has changed is not relivant.
27059  *
27060  * Fork - LGPL
27061  * <script type="text/javascript">
27062  */
27063
27064 /**
27065  * @class Roo.JsonView
27066  * @extends Roo.View
27067  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27068 <pre><code>
27069 var view = new Roo.JsonView({
27070     container: "my-element",
27071     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27072     multiSelect: true, 
27073     jsonRoot: "data" 
27074 });
27075
27076 // listen for node click?
27077 view.on("click", function(vw, index, node, e){
27078     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27079 });
27080
27081 // direct load of JSON data
27082 view.load("foobar.php");
27083
27084 // Example from my blog list
27085 var tpl = new Roo.Template(
27086     '&lt;div class="entry"&gt;' +
27087     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27088     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27089     "&lt;/div&gt;&lt;hr /&gt;"
27090 );
27091
27092 var moreView = new Roo.JsonView({
27093     container :  "entry-list", 
27094     template : tpl,
27095     jsonRoot: "posts"
27096 });
27097 moreView.on("beforerender", this.sortEntries, this);
27098 moreView.load({
27099     url: "/blog/get-posts.php",
27100     params: "allposts=true",
27101     text: "Loading Blog Entries..."
27102 });
27103 </code></pre>
27104
27105 * Note: old code is supported with arguments : (container, template, config)
27106
27107
27108  * @constructor
27109  * Create a new JsonView
27110  * 
27111  * @param {Object} config The config object
27112  * 
27113  */
27114 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27115     
27116     
27117     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27118
27119     var um = this.el.getUpdateManager();
27120     um.setRenderer(this);
27121     um.on("update", this.onLoad, this);
27122     um.on("failure", this.onLoadException, this);
27123
27124     /**
27125      * @event beforerender
27126      * Fires before rendering of the downloaded JSON data.
27127      * @param {Roo.JsonView} this
27128      * @param {Object} data The JSON data loaded
27129      */
27130     /**
27131      * @event load
27132      * Fires when data is loaded.
27133      * @param {Roo.JsonView} this
27134      * @param {Object} data The JSON data loaded
27135      * @param {Object} response The raw Connect response object
27136      */
27137     /**
27138      * @event loadexception
27139      * Fires when loading fails.
27140      * @param {Roo.JsonView} this
27141      * @param {Object} response The raw Connect response object
27142      */
27143     this.addEvents({
27144         'beforerender' : true,
27145         'load' : true,
27146         'loadexception' : true
27147     });
27148 };
27149 Roo.extend(Roo.JsonView, Roo.View, {
27150     /**
27151      * @type {String} The root property in the loaded JSON object that contains the data
27152      */
27153     jsonRoot : "",
27154
27155     /**
27156      * Refreshes the view.
27157      */
27158     refresh : function(){
27159         this.clearSelections();
27160         this.el.update("");
27161         var html = [];
27162         var o = this.jsonData;
27163         if(o && o.length > 0){
27164             for(var i = 0, len = o.length; i < len; i++){
27165                 var data = this.prepareData(o[i], i, o);
27166                 html[html.length] = this.tpl.apply(data);
27167             }
27168         }else{
27169             html.push(this.emptyText);
27170         }
27171         this.el.update(html.join(""));
27172         this.nodes = this.el.dom.childNodes;
27173         this.updateIndexes(0);
27174     },
27175
27176     /**
27177      * 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.
27178      * @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:
27179      <pre><code>
27180      view.load({
27181          url: "your-url.php",
27182          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27183          callback: yourFunction,
27184          scope: yourObject, //(optional scope)
27185          discardUrl: false,
27186          nocache: false,
27187          text: "Loading...",
27188          timeout: 30,
27189          scripts: false
27190      });
27191      </code></pre>
27192      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27193      * 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.
27194      * @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}
27195      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27196      * @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.
27197      */
27198     load : function(){
27199         var um = this.el.getUpdateManager();
27200         um.update.apply(um, arguments);
27201     },
27202
27203     // note - render is a standard framework call...
27204     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27205     render : function(el, response){
27206         
27207         this.clearSelections();
27208         this.el.update("");
27209         var o;
27210         try{
27211             if (response != '') {
27212                 o = Roo.util.JSON.decode(response.responseText);
27213                 if(this.jsonRoot){
27214                     
27215                     o = o[this.jsonRoot];
27216                 }
27217             }
27218         } catch(e){
27219         }
27220         /**
27221          * The current JSON data or null
27222          */
27223         this.jsonData = o;
27224         this.beforeRender();
27225         this.refresh();
27226     },
27227
27228 /**
27229  * Get the number of records in the current JSON dataset
27230  * @return {Number}
27231  */
27232     getCount : function(){
27233         return this.jsonData ? this.jsonData.length : 0;
27234     },
27235
27236 /**
27237  * Returns the JSON object for the specified node(s)
27238  * @param {HTMLElement/Array} node The node or an array of nodes
27239  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27240  * you get the JSON object for the node
27241  */
27242     getNodeData : function(node){
27243         if(node instanceof Array){
27244             var data = [];
27245             for(var i = 0, len = node.length; i < len; i++){
27246                 data.push(this.getNodeData(node[i]));
27247             }
27248             return data;
27249         }
27250         return this.jsonData[this.indexOf(node)] || null;
27251     },
27252
27253     beforeRender : function(){
27254         this.snapshot = this.jsonData;
27255         if(this.sortInfo){
27256             this.sort.apply(this, this.sortInfo);
27257         }
27258         this.fireEvent("beforerender", this, this.jsonData);
27259     },
27260
27261     onLoad : function(el, o){
27262         this.fireEvent("load", this, this.jsonData, o);
27263     },
27264
27265     onLoadException : function(el, o){
27266         this.fireEvent("loadexception", this, o);
27267     },
27268
27269 /**
27270  * Filter the data by a specific property.
27271  * @param {String} property A property on your JSON objects
27272  * @param {String/RegExp} value Either string that the property values
27273  * should start with, or a RegExp to test against the property
27274  */
27275     filter : function(property, value){
27276         if(this.jsonData){
27277             var data = [];
27278             var ss = this.snapshot;
27279             if(typeof value == "string"){
27280                 var vlen = value.length;
27281                 if(vlen == 0){
27282                     this.clearFilter();
27283                     return;
27284                 }
27285                 value = value.toLowerCase();
27286                 for(var i = 0, len = ss.length; i < len; i++){
27287                     var o = ss[i];
27288                     if(o[property].substr(0, vlen).toLowerCase() == value){
27289                         data.push(o);
27290                     }
27291                 }
27292             } else if(value.exec){ // regex?
27293                 for(var i = 0, len = ss.length; i < len; i++){
27294                     var o = ss[i];
27295                     if(value.test(o[property])){
27296                         data.push(o);
27297                     }
27298                 }
27299             } else{
27300                 return;
27301             }
27302             this.jsonData = data;
27303             this.refresh();
27304         }
27305     },
27306
27307 /**
27308  * Filter by a function. The passed function will be called with each
27309  * object in the current dataset. If the function returns true the value is kept,
27310  * otherwise it is filtered.
27311  * @param {Function} fn
27312  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27313  */
27314     filterBy : function(fn, scope){
27315         if(this.jsonData){
27316             var data = [];
27317             var ss = this.snapshot;
27318             for(var i = 0, len = ss.length; i < len; i++){
27319                 var o = ss[i];
27320                 if(fn.call(scope || this, o)){
27321                     data.push(o);
27322                 }
27323             }
27324             this.jsonData = data;
27325             this.refresh();
27326         }
27327     },
27328
27329 /**
27330  * Clears the current filter.
27331  */
27332     clearFilter : function(){
27333         if(this.snapshot && this.jsonData != this.snapshot){
27334             this.jsonData = this.snapshot;
27335             this.refresh();
27336         }
27337     },
27338
27339
27340 /**
27341  * Sorts the data for this view and refreshes it.
27342  * @param {String} property A property on your JSON objects to sort on
27343  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27344  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27345  */
27346     sort : function(property, dir, sortType){
27347         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27348         if(this.jsonData){
27349             var p = property;
27350             var dsc = dir && dir.toLowerCase() == "desc";
27351             var f = function(o1, o2){
27352                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27353                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27354                 ;
27355                 if(v1 < v2){
27356                     return dsc ? +1 : -1;
27357                 } else if(v1 > v2){
27358                     return dsc ? -1 : +1;
27359                 } else{
27360                     return 0;
27361                 }
27362             };
27363             this.jsonData.sort(f);
27364             this.refresh();
27365             if(this.jsonData != this.snapshot){
27366                 this.snapshot.sort(f);
27367             }
27368         }
27369     }
27370 });/*
27371  * Based on:
27372  * Ext JS Library 1.1.1
27373  * Copyright(c) 2006-2007, Ext JS, LLC.
27374  *
27375  * Originally Released Under LGPL - original licence link has changed is not relivant.
27376  *
27377  * Fork - LGPL
27378  * <script type="text/javascript">
27379  */
27380  
27381
27382 /**
27383  * @class Roo.ColorPalette
27384  * @extends Roo.Component
27385  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27386  * Here's an example of typical usage:
27387  * <pre><code>
27388 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27389 cp.render('my-div');
27390
27391 cp.on('select', function(palette, selColor){
27392     // do something with selColor
27393 });
27394 </code></pre>
27395  * @constructor
27396  * Create a new ColorPalette
27397  * @param {Object} config The config object
27398  */
27399 Roo.ColorPalette = function(config){
27400     Roo.ColorPalette.superclass.constructor.call(this, config);
27401     this.addEvents({
27402         /**
27403              * @event select
27404              * Fires when a color is selected
27405              * @param {ColorPalette} this
27406              * @param {String} color The 6-digit color hex code (without the # symbol)
27407              */
27408         select: true
27409     });
27410
27411     if(this.handler){
27412         this.on("select", this.handler, this.scope, true);
27413     }
27414 };
27415 Roo.extend(Roo.ColorPalette, Roo.Component, {
27416     /**
27417      * @cfg {String} itemCls
27418      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27419      */
27420     itemCls : "x-color-palette",
27421     /**
27422      * @cfg {String} value
27423      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27424      * the hex codes are case-sensitive.
27425      */
27426     value : null,
27427     clickEvent:'click',
27428     // private
27429     ctype: "Roo.ColorPalette",
27430
27431     /**
27432      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27433      */
27434     allowReselect : false,
27435
27436     /**
27437      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27438      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27439      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27440      * of colors with the width setting until the box is symmetrical.</p>
27441      * <p>You can override individual colors if needed:</p>
27442      * <pre><code>
27443 var cp = new Roo.ColorPalette();
27444 cp.colors[0] = "FF0000";  // change the first box to red
27445 </code></pre>
27446
27447 Or you can provide a custom array of your own for complete control:
27448 <pre><code>
27449 var cp = new Roo.ColorPalette();
27450 cp.colors = ["000000", "993300", "333300"];
27451 </code></pre>
27452      * @type Array
27453      */
27454     colors : [
27455         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27456         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27457         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27458         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27459         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27460     ],
27461
27462     // private
27463     onRender : function(container, position){
27464         var t = new Roo.MasterTemplate(
27465             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27466         );
27467         var c = this.colors;
27468         for(var i = 0, len = c.length; i < len; i++){
27469             t.add([c[i]]);
27470         }
27471         var el = document.createElement("div");
27472         el.className = this.itemCls;
27473         t.overwrite(el);
27474         container.dom.insertBefore(el, position);
27475         this.el = Roo.get(el);
27476         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27477         if(this.clickEvent != 'click'){
27478             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27479         }
27480     },
27481
27482     // private
27483     afterRender : function(){
27484         Roo.ColorPalette.superclass.afterRender.call(this);
27485         if(this.value){
27486             var s = this.value;
27487             this.value = null;
27488             this.select(s);
27489         }
27490     },
27491
27492     // private
27493     handleClick : function(e, t){
27494         e.preventDefault();
27495         if(!this.disabled){
27496             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27497             this.select(c.toUpperCase());
27498         }
27499     },
27500
27501     /**
27502      * Selects the specified color in the palette (fires the select event)
27503      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27504      */
27505     select : function(color){
27506         color = color.replace("#", "");
27507         if(color != this.value || this.allowReselect){
27508             var el = this.el;
27509             if(this.value){
27510                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27511             }
27512             el.child("a.color-"+color).addClass("x-color-palette-sel");
27513             this.value = color;
27514             this.fireEvent("select", this, color);
27515         }
27516     }
27517 });/*
27518  * Based on:
27519  * Ext JS Library 1.1.1
27520  * Copyright(c) 2006-2007, Ext JS, LLC.
27521  *
27522  * Originally Released Under LGPL - original licence link has changed is not relivant.
27523  *
27524  * Fork - LGPL
27525  * <script type="text/javascript">
27526  */
27527  
27528 /**
27529  * @class Roo.DatePicker
27530  * @extends Roo.Component
27531  * Simple date picker class.
27532  * @constructor
27533  * Create a new DatePicker
27534  * @param {Object} config The config object
27535  */
27536 Roo.DatePicker = function(config){
27537     Roo.DatePicker.superclass.constructor.call(this, config);
27538
27539     this.value = config && config.value ?
27540                  config.value.clearTime() : new Date().clearTime();
27541
27542     this.addEvents({
27543         /**
27544              * @event select
27545              * Fires when a date is selected
27546              * @param {DatePicker} this
27547              * @param {Date} date The selected date
27548              */
27549         'select': true,
27550         /**
27551              * @event monthchange
27552              * Fires when the displayed month changes 
27553              * @param {DatePicker} this
27554              * @param {Date} date The selected month
27555              */
27556         'monthchange': true
27557     });
27558
27559     if(this.handler){
27560         this.on("select", this.handler,  this.scope || this);
27561     }
27562     // build the disabledDatesRE
27563     if(!this.disabledDatesRE && this.disabledDates){
27564         var dd = this.disabledDates;
27565         var re = "(?:";
27566         for(var i = 0; i < dd.length; i++){
27567             re += dd[i];
27568             if(i != dd.length-1) {
27569                 re += "|";
27570             }
27571         }
27572         this.disabledDatesRE = new RegExp(re + ")");
27573     }
27574 };
27575
27576 Roo.extend(Roo.DatePicker, Roo.Component, {
27577     /**
27578      * @cfg {String} todayText
27579      * The text to display on the button that selects the current date (defaults to "Today")
27580      */
27581     todayText : "Today",
27582     /**
27583      * @cfg {String} okText
27584      * The text to display on the ok button
27585      */
27586     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27587     /**
27588      * @cfg {String} cancelText
27589      * The text to display on the cancel button
27590      */
27591     cancelText : "Cancel",
27592     /**
27593      * @cfg {String} todayTip
27594      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27595      */
27596     todayTip : "{0} (Spacebar)",
27597     /**
27598      * @cfg {Date} minDate
27599      * Minimum allowable date (JavaScript date object, defaults to null)
27600      */
27601     minDate : null,
27602     /**
27603      * @cfg {Date} maxDate
27604      * Maximum allowable date (JavaScript date object, defaults to null)
27605      */
27606     maxDate : null,
27607     /**
27608      * @cfg {String} minText
27609      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27610      */
27611     minText : "This date is before the minimum date",
27612     /**
27613      * @cfg {String} maxText
27614      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27615      */
27616     maxText : "This date is after the maximum date",
27617     /**
27618      * @cfg {String} format
27619      * The default date format string which can be overriden for localization support.  The format must be
27620      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27621      */
27622     format : "m/d/y",
27623     /**
27624      * @cfg {Array} disabledDays
27625      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27626      */
27627     disabledDays : null,
27628     /**
27629      * @cfg {String} disabledDaysText
27630      * The tooltip to display when the date falls on a disabled day (defaults to "")
27631      */
27632     disabledDaysText : "",
27633     /**
27634      * @cfg {RegExp} disabledDatesRE
27635      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27636      */
27637     disabledDatesRE : null,
27638     /**
27639      * @cfg {String} disabledDatesText
27640      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27641      */
27642     disabledDatesText : "",
27643     /**
27644      * @cfg {Boolean} constrainToViewport
27645      * True to constrain the date picker to the viewport (defaults to true)
27646      */
27647     constrainToViewport : true,
27648     /**
27649      * @cfg {Array} monthNames
27650      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27651      */
27652     monthNames : Date.monthNames,
27653     /**
27654      * @cfg {Array} dayNames
27655      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27656      */
27657     dayNames : Date.dayNames,
27658     /**
27659      * @cfg {String} nextText
27660      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27661      */
27662     nextText: 'Next Month (Control+Right)',
27663     /**
27664      * @cfg {String} prevText
27665      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27666      */
27667     prevText: 'Previous Month (Control+Left)',
27668     /**
27669      * @cfg {String} monthYearText
27670      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27671      */
27672     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27673     /**
27674      * @cfg {Number} startDay
27675      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27676      */
27677     startDay : 0,
27678     /**
27679      * @cfg {Bool} showClear
27680      * Show a clear button (usefull for date form elements that can be blank.)
27681      */
27682     
27683     showClear: false,
27684     
27685     /**
27686      * Sets the value of the date field
27687      * @param {Date} value The date to set
27688      */
27689     setValue : function(value){
27690         var old = this.value;
27691         
27692         if (typeof(value) == 'string') {
27693          
27694             value = Date.parseDate(value, this.format);
27695         }
27696         if (!value) {
27697             value = new Date();
27698         }
27699         
27700         this.value = value.clearTime(true);
27701         if(this.el){
27702             this.update(this.value);
27703         }
27704     },
27705
27706     /**
27707      * Gets the current selected value of the date field
27708      * @return {Date} The selected date
27709      */
27710     getValue : function(){
27711         return this.value;
27712     },
27713
27714     // private
27715     focus : function(){
27716         if(this.el){
27717             this.update(this.activeDate);
27718         }
27719     },
27720
27721     // privateval
27722     onRender : function(container, position){
27723         
27724         var m = [
27725              '<table cellspacing="0">',
27726                 '<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>',
27727                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27728         var dn = this.dayNames;
27729         for(var i = 0; i < 7; i++){
27730             var d = this.startDay+i;
27731             if(d > 6){
27732                 d = d-7;
27733             }
27734             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27735         }
27736         m[m.length] = "</tr></thead><tbody><tr>";
27737         for(var i = 0; i < 42; i++) {
27738             if(i % 7 == 0 && i != 0){
27739                 m[m.length] = "</tr><tr>";
27740             }
27741             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27742         }
27743         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27744             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27745
27746         var el = document.createElement("div");
27747         el.className = "x-date-picker";
27748         el.innerHTML = m.join("");
27749
27750         container.dom.insertBefore(el, position);
27751
27752         this.el = Roo.get(el);
27753         this.eventEl = Roo.get(el.firstChild);
27754
27755         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
27756             handler: this.showPrevMonth,
27757             scope: this,
27758             preventDefault:true,
27759             stopDefault:true
27760         });
27761
27762         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
27763             handler: this.showNextMonth,
27764             scope: this,
27765             preventDefault:true,
27766             stopDefault:true
27767         });
27768
27769         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
27770
27771         this.monthPicker = this.el.down('div.x-date-mp');
27772         this.monthPicker.enableDisplayMode('block');
27773         
27774         var kn = new Roo.KeyNav(this.eventEl, {
27775             "left" : function(e){
27776                 e.ctrlKey ?
27777                     this.showPrevMonth() :
27778                     this.update(this.activeDate.add("d", -1));
27779             },
27780
27781             "right" : function(e){
27782                 e.ctrlKey ?
27783                     this.showNextMonth() :
27784                     this.update(this.activeDate.add("d", 1));
27785             },
27786
27787             "up" : function(e){
27788                 e.ctrlKey ?
27789                     this.showNextYear() :
27790                     this.update(this.activeDate.add("d", -7));
27791             },
27792
27793             "down" : function(e){
27794                 e.ctrlKey ?
27795                     this.showPrevYear() :
27796                     this.update(this.activeDate.add("d", 7));
27797             },
27798
27799             "pageUp" : function(e){
27800                 this.showNextMonth();
27801             },
27802
27803             "pageDown" : function(e){
27804                 this.showPrevMonth();
27805             },
27806
27807             "enter" : function(e){
27808                 e.stopPropagation();
27809                 return true;
27810             },
27811
27812             scope : this
27813         });
27814
27815         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
27816
27817         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
27818
27819         this.el.unselectable();
27820         
27821         this.cells = this.el.select("table.x-date-inner tbody td");
27822         this.textNodes = this.el.query("table.x-date-inner tbody span");
27823
27824         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
27825             text: "&#160;",
27826             tooltip: this.monthYearText
27827         });
27828
27829         this.mbtn.on('click', this.showMonthPicker, this);
27830         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
27831
27832
27833         var today = (new Date()).dateFormat(this.format);
27834         
27835         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
27836         if (this.showClear) {
27837             baseTb.add( new Roo.Toolbar.Fill());
27838         }
27839         baseTb.add({
27840             text: String.format(this.todayText, today),
27841             tooltip: String.format(this.todayTip, today),
27842             handler: this.selectToday,
27843             scope: this
27844         });
27845         
27846         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
27847             
27848         //});
27849         if (this.showClear) {
27850             
27851             baseTb.add( new Roo.Toolbar.Fill());
27852             baseTb.add({
27853                 text: '&#160;',
27854                 cls: 'x-btn-icon x-btn-clear',
27855                 handler: function() {
27856                     //this.value = '';
27857                     this.fireEvent("select", this, '');
27858                 },
27859                 scope: this
27860             });
27861         }
27862         
27863         
27864         if(Roo.isIE){
27865             this.el.repaint();
27866         }
27867         this.update(this.value);
27868     },
27869
27870     createMonthPicker : function(){
27871         if(!this.monthPicker.dom.firstChild){
27872             var buf = ['<table border="0" cellspacing="0">'];
27873             for(var i = 0; i < 6; i++){
27874                 buf.push(
27875                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
27876                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
27877                     i == 0 ?
27878                     '<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>' :
27879                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
27880                 );
27881             }
27882             buf.push(
27883                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
27884                     this.okText,
27885                     '</button><button type="button" class="x-date-mp-cancel">',
27886                     this.cancelText,
27887                     '</button></td></tr>',
27888                 '</table>'
27889             );
27890             this.monthPicker.update(buf.join(''));
27891             this.monthPicker.on('click', this.onMonthClick, this);
27892             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
27893
27894             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
27895             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
27896
27897             this.mpMonths.each(function(m, a, i){
27898                 i += 1;
27899                 if((i%2) == 0){
27900                     m.dom.xmonth = 5 + Math.round(i * .5);
27901                 }else{
27902                     m.dom.xmonth = Math.round((i-1) * .5);
27903                 }
27904             });
27905         }
27906     },
27907
27908     showMonthPicker : function(){
27909         this.createMonthPicker();
27910         var size = this.el.getSize();
27911         this.monthPicker.setSize(size);
27912         this.monthPicker.child('table').setSize(size);
27913
27914         this.mpSelMonth = (this.activeDate || this.value).getMonth();
27915         this.updateMPMonth(this.mpSelMonth);
27916         this.mpSelYear = (this.activeDate || this.value).getFullYear();
27917         this.updateMPYear(this.mpSelYear);
27918
27919         this.monthPicker.slideIn('t', {duration:.2});
27920     },
27921
27922     updateMPYear : function(y){
27923         this.mpyear = y;
27924         var ys = this.mpYears.elements;
27925         for(var i = 1; i <= 10; i++){
27926             var td = ys[i-1], y2;
27927             if((i%2) == 0){
27928                 y2 = y + Math.round(i * .5);
27929                 td.firstChild.innerHTML = y2;
27930                 td.xyear = y2;
27931             }else{
27932                 y2 = y - (5-Math.round(i * .5));
27933                 td.firstChild.innerHTML = y2;
27934                 td.xyear = y2;
27935             }
27936             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
27937         }
27938     },
27939
27940     updateMPMonth : function(sm){
27941         this.mpMonths.each(function(m, a, i){
27942             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
27943         });
27944     },
27945
27946     selectMPMonth: function(m){
27947         
27948     },
27949
27950     onMonthClick : function(e, t){
27951         e.stopEvent();
27952         var el = new Roo.Element(t), pn;
27953         if(el.is('button.x-date-mp-cancel')){
27954             this.hideMonthPicker();
27955         }
27956         else if(el.is('button.x-date-mp-ok')){
27957             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
27958             this.hideMonthPicker();
27959         }
27960         else if(pn = el.up('td.x-date-mp-month', 2)){
27961             this.mpMonths.removeClass('x-date-mp-sel');
27962             pn.addClass('x-date-mp-sel');
27963             this.mpSelMonth = pn.dom.xmonth;
27964         }
27965         else if(pn = el.up('td.x-date-mp-year', 2)){
27966             this.mpYears.removeClass('x-date-mp-sel');
27967             pn.addClass('x-date-mp-sel');
27968             this.mpSelYear = pn.dom.xyear;
27969         }
27970         else if(el.is('a.x-date-mp-prev')){
27971             this.updateMPYear(this.mpyear-10);
27972         }
27973         else if(el.is('a.x-date-mp-next')){
27974             this.updateMPYear(this.mpyear+10);
27975         }
27976     },
27977
27978     onMonthDblClick : function(e, t){
27979         e.stopEvent();
27980         var el = new Roo.Element(t), pn;
27981         if(pn = el.up('td.x-date-mp-month', 2)){
27982             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
27983             this.hideMonthPicker();
27984         }
27985         else if(pn = el.up('td.x-date-mp-year', 2)){
27986             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
27987             this.hideMonthPicker();
27988         }
27989     },
27990
27991     hideMonthPicker : function(disableAnim){
27992         if(this.monthPicker){
27993             if(disableAnim === true){
27994                 this.monthPicker.hide();
27995             }else{
27996                 this.monthPicker.slideOut('t', {duration:.2});
27997             }
27998         }
27999     },
28000
28001     // private
28002     showPrevMonth : function(e){
28003         this.update(this.activeDate.add("mo", -1));
28004     },
28005
28006     // private
28007     showNextMonth : function(e){
28008         this.update(this.activeDate.add("mo", 1));
28009     },
28010
28011     // private
28012     showPrevYear : function(){
28013         this.update(this.activeDate.add("y", -1));
28014     },
28015
28016     // private
28017     showNextYear : function(){
28018         this.update(this.activeDate.add("y", 1));
28019     },
28020
28021     // private
28022     handleMouseWheel : function(e){
28023         var delta = e.getWheelDelta();
28024         if(delta > 0){
28025             this.showPrevMonth();
28026             e.stopEvent();
28027         } else if(delta < 0){
28028             this.showNextMonth();
28029             e.stopEvent();
28030         }
28031     },
28032
28033     // private
28034     handleDateClick : function(e, t){
28035         e.stopEvent();
28036         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28037             this.setValue(new Date(t.dateValue));
28038             this.fireEvent("select", this, this.value);
28039         }
28040     },
28041
28042     // private
28043     selectToday : function(){
28044         this.setValue(new Date().clearTime());
28045         this.fireEvent("select", this, this.value);
28046     },
28047
28048     // private
28049     update : function(date)
28050     {
28051         var vd = this.activeDate;
28052         this.activeDate = date;
28053         if(vd && this.el){
28054             var t = date.getTime();
28055             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28056                 this.cells.removeClass("x-date-selected");
28057                 this.cells.each(function(c){
28058                    if(c.dom.firstChild.dateValue == t){
28059                        c.addClass("x-date-selected");
28060                        setTimeout(function(){
28061                             try{c.dom.firstChild.focus();}catch(e){}
28062                        }, 50);
28063                        return false;
28064                    }
28065                 });
28066                 return;
28067             }
28068         }
28069         
28070         var days = date.getDaysInMonth();
28071         var firstOfMonth = date.getFirstDateOfMonth();
28072         var startingPos = firstOfMonth.getDay()-this.startDay;
28073
28074         if(startingPos <= this.startDay){
28075             startingPos += 7;
28076         }
28077
28078         var pm = date.add("mo", -1);
28079         var prevStart = pm.getDaysInMonth()-startingPos;
28080
28081         var cells = this.cells.elements;
28082         var textEls = this.textNodes;
28083         days += startingPos;
28084
28085         // convert everything to numbers so it's fast
28086         var day = 86400000;
28087         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28088         var today = new Date().clearTime().getTime();
28089         var sel = date.clearTime().getTime();
28090         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28091         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28092         var ddMatch = this.disabledDatesRE;
28093         var ddText = this.disabledDatesText;
28094         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28095         var ddaysText = this.disabledDaysText;
28096         var format = this.format;
28097
28098         var setCellClass = function(cal, cell){
28099             cell.title = "";
28100             var t = d.getTime();
28101             cell.firstChild.dateValue = t;
28102             if(t == today){
28103                 cell.className += " x-date-today";
28104                 cell.title = cal.todayText;
28105             }
28106             if(t == sel){
28107                 cell.className += " x-date-selected";
28108                 setTimeout(function(){
28109                     try{cell.firstChild.focus();}catch(e){}
28110                 }, 50);
28111             }
28112             // disabling
28113             if(t < min) {
28114                 cell.className = " x-date-disabled";
28115                 cell.title = cal.minText;
28116                 return;
28117             }
28118             if(t > max) {
28119                 cell.className = " x-date-disabled";
28120                 cell.title = cal.maxText;
28121                 return;
28122             }
28123             if(ddays){
28124                 if(ddays.indexOf(d.getDay()) != -1){
28125                     cell.title = ddaysText;
28126                     cell.className = " x-date-disabled";
28127                 }
28128             }
28129             if(ddMatch && format){
28130                 var fvalue = d.dateFormat(format);
28131                 if(ddMatch.test(fvalue)){
28132                     cell.title = ddText.replace("%0", fvalue);
28133                     cell.className = " x-date-disabled";
28134                 }
28135             }
28136         };
28137
28138         var i = 0;
28139         for(; i < startingPos; i++) {
28140             textEls[i].innerHTML = (++prevStart);
28141             d.setDate(d.getDate()+1);
28142             cells[i].className = "x-date-prevday";
28143             setCellClass(this, cells[i]);
28144         }
28145         for(; i < days; i++){
28146             intDay = i - startingPos + 1;
28147             textEls[i].innerHTML = (intDay);
28148             d.setDate(d.getDate()+1);
28149             cells[i].className = "x-date-active";
28150             setCellClass(this, cells[i]);
28151         }
28152         var extraDays = 0;
28153         for(; i < 42; i++) {
28154              textEls[i].innerHTML = (++extraDays);
28155              d.setDate(d.getDate()+1);
28156              cells[i].className = "x-date-nextday";
28157              setCellClass(this, cells[i]);
28158         }
28159
28160         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28161         this.fireEvent('monthchange', this, date);
28162         
28163         if(!this.internalRender){
28164             var main = this.el.dom.firstChild;
28165             var w = main.offsetWidth;
28166             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28167             Roo.fly(main).setWidth(w);
28168             this.internalRender = true;
28169             // opera does not respect the auto grow header center column
28170             // then, after it gets a width opera refuses to recalculate
28171             // without a second pass
28172             if(Roo.isOpera && !this.secondPass){
28173                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28174                 this.secondPass = true;
28175                 this.update.defer(10, this, [date]);
28176             }
28177         }
28178         
28179         
28180     }
28181 });        /*
28182  * Based on:
28183  * Ext JS Library 1.1.1
28184  * Copyright(c) 2006-2007, Ext JS, LLC.
28185  *
28186  * Originally Released Under LGPL - original licence link has changed is not relivant.
28187  *
28188  * Fork - LGPL
28189  * <script type="text/javascript">
28190  */
28191 /**
28192  * @class Roo.TabPanel
28193  * @extends Roo.util.Observable
28194  * A lightweight tab container.
28195  * <br><br>
28196  * Usage:
28197  * <pre><code>
28198 // basic tabs 1, built from existing content
28199 var tabs = new Roo.TabPanel("tabs1");
28200 tabs.addTab("script", "View Script");
28201 tabs.addTab("markup", "View Markup");
28202 tabs.activate("script");
28203
28204 // more advanced tabs, built from javascript
28205 var jtabs = new Roo.TabPanel("jtabs");
28206 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28207
28208 // set up the UpdateManager
28209 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28210 var updater = tab2.getUpdateManager();
28211 updater.setDefaultUrl("ajax1.htm");
28212 tab2.on('activate', updater.refresh, updater, true);
28213
28214 // Use setUrl for Ajax loading
28215 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28216 tab3.setUrl("ajax2.htm", null, true);
28217
28218 // Disabled tab
28219 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28220 tab4.disable();
28221
28222 jtabs.activate("jtabs-1");
28223  * </code></pre>
28224  * @constructor
28225  * Create a new TabPanel.
28226  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28227  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28228  */
28229 Roo.TabPanel = function(container, config){
28230     /**
28231     * The container element for this TabPanel.
28232     * @type Roo.Element
28233     */
28234     this.el = Roo.get(container, true);
28235     if(config){
28236         if(typeof config == "boolean"){
28237             this.tabPosition = config ? "bottom" : "top";
28238         }else{
28239             Roo.apply(this, config);
28240         }
28241     }
28242     if(this.tabPosition == "bottom"){
28243         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28244         this.el.addClass("x-tabs-bottom");
28245     }
28246     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28247     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28248     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28249     if(Roo.isIE){
28250         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28251     }
28252     if(this.tabPosition != "bottom"){
28253         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28254          * @type Roo.Element
28255          */
28256         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28257         this.el.addClass("x-tabs-top");
28258     }
28259     this.items = [];
28260
28261     this.bodyEl.setStyle("position", "relative");
28262
28263     this.active = null;
28264     this.activateDelegate = this.activate.createDelegate(this);
28265
28266     this.addEvents({
28267         /**
28268          * @event tabchange
28269          * Fires when the active tab changes
28270          * @param {Roo.TabPanel} this
28271          * @param {Roo.TabPanelItem} activePanel The new active tab
28272          */
28273         "tabchange": true,
28274         /**
28275          * @event beforetabchange
28276          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28277          * @param {Roo.TabPanel} this
28278          * @param {Object} e Set cancel to true on this object to cancel the tab change
28279          * @param {Roo.TabPanelItem} tab The tab being changed to
28280          */
28281         "beforetabchange" : true
28282     });
28283
28284     Roo.EventManager.onWindowResize(this.onResize, this);
28285     this.cpad = this.el.getPadding("lr");
28286     this.hiddenCount = 0;
28287
28288
28289     // toolbar on the tabbar support...
28290     if (this.toolbar) {
28291         var tcfg = this.toolbar;
28292         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28293         this.toolbar = new Roo.Toolbar(tcfg);
28294         if (Roo.isSafari) {
28295             var tbl = tcfg.container.child('table', true);
28296             tbl.setAttribute('width', '100%');
28297         }
28298         
28299     }
28300    
28301
28302
28303     Roo.TabPanel.superclass.constructor.call(this);
28304 };
28305
28306 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28307     /*
28308      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28309      */
28310     tabPosition : "top",
28311     /*
28312      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28313      */
28314     currentTabWidth : 0,
28315     /*
28316      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28317      */
28318     minTabWidth : 40,
28319     /*
28320      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28321      */
28322     maxTabWidth : 250,
28323     /*
28324      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28325      */
28326     preferredTabWidth : 175,
28327     /*
28328      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28329      */
28330     resizeTabs : false,
28331     /*
28332      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28333      */
28334     monitorResize : true,
28335     /*
28336      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28337      */
28338     toolbar : false,
28339
28340     /**
28341      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28342      * @param {String} id The id of the div to use <b>or create</b>
28343      * @param {String} text The text for the tab
28344      * @param {String} content (optional) Content to put in the TabPanelItem body
28345      * @param {Boolean} closable (optional) True to create a close icon on the tab
28346      * @return {Roo.TabPanelItem} The created TabPanelItem
28347      */
28348     addTab : function(id, text, content, closable){
28349         var item = new Roo.TabPanelItem(this, id, text, closable);
28350         this.addTabItem(item);
28351         if(content){
28352             item.setContent(content);
28353         }
28354         return item;
28355     },
28356
28357     /**
28358      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28359      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28360      * @return {Roo.TabPanelItem}
28361      */
28362     getTab : function(id){
28363         return this.items[id];
28364     },
28365
28366     /**
28367      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28368      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28369      */
28370     hideTab : function(id){
28371         var t = this.items[id];
28372         if(!t.isHidden()){
28373            t.setHidden(true);
28374            this.hiddenCount++;
28375            this.autoSizeTabs();
28376         }
28377     },
28378
28379     /**
28380      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28381      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28382      */
28383     unhideTab : function(id){
28384         var t = this.items[id];
28385         if(t.isHidden()){
28386            t.setHidden(false);
28387            this.hiddenCount--;
28388            this.autoSizeTabs();
28389         }
28390     },
28391
28392     /**
28393      * Adds an existing {@link Roo.TabPanelItem}.
28394      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28395      */
28396     addTabItem : function(item){
28397         this.items[item.id] = item;
28398         this.items.push(item);
28399         if(this.resizeTabs){
28400            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28401            this.autoSizeTabs();
28402         }else{
28403             item.autoSize();
28404         }
28405     },
28406
28407     /**
28408      * Removes a {@link Roo.TabPanelItem}.
28409      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28410      */
28411     removeTab : function(id){
28412         var items = this.items;
28413         var tab = items[id];
28414         if(!tab) { return; }
28415         var index = items.indexOf(tab);
28416         if(this.active == tab && items.length > 1){
28417             var newTab = this.getNextAvailable(index);
28418             if(newTab) {
28419                 newTab.activate();
28420             }
28421         }
28422         this.stripEl.dom.removeChild(tab.pnode.dom);
28423         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28424             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28425         }
28426         items.splice(index, 1);
28427         delete this.items[tab.id];
28428         tab.fireEvent("close", tab);
28429         tab.purgeListeners();
28430         this.autoSizeTabs();
28431     },
28432
28433     getNextAvailable : function(start){
28434         var items = this.items;
28435         var index = start;
28436         // look for a next tab that will slide over to
28437         // replace the one being removed
28438         while(index < items.length){
28439             var item = items[++index];
28440             if(item && !item.isHidden()){
28441                 return item;
28442             }
28443         }
28444         // if one isn't found select the previous tab (on the left)
28445         index = start;
28446         while(index >= 0){
28447             var item = items[--index];
28448             if(item && !item.isHidden()){
28449                 return item;
28450             }
28451         }
28452         return null;
28453     },
28454
28455     /**
28456      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28457      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28458      */
28459     disableTab : function(id){
28460         var tab = this.items[id];
28461         if(tab && this.active != tab){
28462             tab.disable();
28463         }
28464     },
28465
28466     /**
28467      * Enables a {@link Roo.TabPanelItem} that is disabled.
28468      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28469      */
28470     enableTab : function(id){
28471         var tab = this.items[id];
28472         tab.enable();
28473     },
28474
28475     /**
28476      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28477      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28478      * @return {Roo.TabPanelItem} The TabPanelItem.
28479      */
28480     activate : function(id){
28481         var tab = this.items[id];
28482         if(!tab){
28483             return null;
28484         }
28485         if(tab == this.active || tab.disabled){
28486             return tab;
28487         }
28488         var e = {};
28489         this.fireEvent("beforetabchange", this, e, tab);
28490         if(e.cancel !== true && !tab.disabled){
28491             if(this.active){
28492                 this.active.hide();
28493             }
28494             this.active = this.items[id];
28495             this.active.show();
28496             this.fireEvent("tabchange", this, this.active);
28497         }
28498         return tab;
28499     },
28500
28501     /**
28502      * Gets the active {@link Roo.TabPanelItem}.
28503      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28504      */
28505     getActiveTab : function(){
28506         return this.active;
28507     },
28508
28509     /**
28510      * Updates the tab body element to fit the height of the container element
28511      * for overflow scrolling
28512      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28513      */
28514     syncHeight : function(targetHeight){
28515         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28516         var bm = this.bodyEl.getMargins();
28517         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28518         this.bodyEl.setHeight(newHeight);
28519         return newHeight;
28520     },
28521
28522     onResize : function(){
28523         if(this.monitorResize){
28524             this.autoSizeTabs();
28525         }
28526     },
28527
28528     /**
28529      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28530      */
28531     beginUpdate : function(){
28532         this.updating = true;
28533     },
28534
28535     /**
28536      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28537      */
28538     endUpdate : function(){
28539         this.updating = false;
28540         this.autoSizeTabs();
28541     },
28542
28543     /**
28544      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28545      */
28546     autoSizeTabs : function(){
28547         var count = this.items.length;
28548         var vcount = count - this.hiddenCount;
28549         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28550             return;
28551         }
28552         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28553         var availWidth = Math.floor(w / vcount);
28554         var b = this.stripBody;
28555         if(b.getWidth() > w){
28556             var tabs = this.items;
28557             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28558             if(availWidth < this.minTabWidth){
28559                 /*if(!this.sleft){    // incomplete scrolling code
28560                     this.createScrollButtons();
28561                 }
28562                 this.showScroll();
28563                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28564             }
28565         }else{
28566             if(this.currentTabWidth < this.preferredTabWidth){
28567                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28568             }
28569         }
28570     },
28571
28572     /**
28573      * Returns the number of tabs in this TabPanel.
28574      * @return {Number}
28575      */
28576      getCount : function(){
28577          return this.items.length;
28578      },
28579
28580     /**
28581      * Resizes all the tabs to the passed width
28582      * @param {Number} The new width
28583      */
28584     setTabWidth : function(width){
28585         this.currentTabWidth = width;
28586         for(var i = 0, len = this.items.length; i < len; i++) {
28587                 if(!this.items[i].isHidden()) {
28588                 this.items[i].setWidth(width);
28589             }
28590         }
28591     },
28592
28593     /**
28594      * Destroys this TabPanel
28595      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28596      */
28597     destroy : function(removeEl){
28598         Roo.EventManager.removeResizeListener(this.onResize, this);
28599         for(var i = 0, len = this.items.length; i < len; i++){
28600             this.items[i].purgeListeners();
28601         }
28602         if(removeEl === true){
28603             this.el.update("");
28604             this.el.remove();
28605         }
28606     }
28607 });
28608
28609 /**
28610  * @class Roo.TabPanelItem
28611  * @extends Roo.util.Observable
28612  * Represents an individual item (tab plus body) in a TabPanel.
28613  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28614  * @param {String} id The id of this TabPanelItem
28615  * @param {String} text The text for the tab of this TabPanelItem
28616  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28617  */
28618 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28619     /**
28620      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28621      * @type Roo.TabPanel
28622      */
28623     this.tabPanel = tabPanel;
28624     /**
28625      * The id for this TabPanelItem
28626      * @type String
28627      */
28628     this.id = id;
28629     /** @private */
28630     this.disabled = false;
28631     /** @private */
28632     this.text = text;
28633     /** @private */
28634     this.loaded = false;
28635     this.closable = closable;
28636
28637     /**
28638      * The body element for this TabPanelItem.
28639      * @type Roo.Element
28640      */
28641     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28642     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28643     this.bodyEl.setStyle("display", "block");
28644     this.bodyEl.setStyle("zoom", "1");
28645     this.hideAction();
28646
28647     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28648     /** @private */
28649     this.el = Roo.get(els.el, true);
28650     this.inner = Roo.get(els.inner, true);
28651     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28652     this.pnode = Roo.get(els.el.parentNode, true);
28653     this.el.on("mousedown", this.onTabMouseDown, this);
28654     this.el.on("click", this.onTabClick, this);
28655     /** @private */
28656     if(closable){
28657         var c = Roo.get(els.close, true);
28658         c.dom.title = this.closeText;
28659         c.addClassOnOver("close-over");
28660         c.on("click", this.closeClick, this);
28661      }
28662
28663     this.addEvents({
28664          /**
28665          * @event activate
28666          * Fires when this tab becomes the active tab.
28667          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28668          * @param {Roo.TabPanelItem} this
28669          */
28670         "activate": true,
28671         /**
28672          * @event beforeclose
28673          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28674          * @param {Roo.TabPanelItem} this
28675          * @param {Object} e Set cancel to true on this object to cancel the close.
28676          */
28677         "beforeclose": true,
28678         /**
28679          * @event close
28680          * Fires when this tab is closed.
28681          * @param {Roo.TabPanelItem} this
28682          */
28683          "close": true,
28684         /**
28685          * @event deactivate
28686          * Fires when this tab is no longer the active tab.
28687          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28688          * @param {Roo.TabPanelItem} this
28689          */
28690          "deactivate" : true
28691     });
28692     this.hidden = false;
28693
28694     Roo.TabPanelItem.superclass.constructor.call(this);
28695 };
28696
28697 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28698     purgeListeners : function(){
28699        Roo.util.Observable.prototype.purgeListeners.call(this);
28700        this.el.removeAllListeners();
28701     },
28702     /**
28703      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28704      */
28705     show : function(){
28706         this.pnode.addClass("on");
28707         this.showAction();
28708         if(Roo.isOpera){
28709             this.tabPanel.stripWrap.repaint();
28710         }
28711         this.fireEvent("activate", this.tabPanel, this);
28712     },
28713
28714     /**
28715      * Returns true if this tab is the active tab.
28716      * @return {Boolean}
28717      */
28718     isActive : function(){
28719         return this.tabPanel.getActiveTab() == this;
28720     },
28721
28722     /**
28723      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28724      */
28725     hide : function(){
28726         this.pnode.removeClass("on");
28727         this.hideAction();
28728         this.fireEvent("deactivate", this.tabPanel, this);
28729     },
28730
28731     hideAction : function(){
28732         this.bodyEl.hide();
28733         this.bodyEl.setStyle("position", "absolute");
28734         this.bodyEl.setLeft("-20000px");
28735         this.bodyEl.setTop("-20000px");
28736     },
28737
28738     showAction : function(){
28739         this.bodyEl.setStyle("position", "relative");
28740         this.bodyEl.setTop("");
28741         this.bodyEl.setLeft("");
28742         this.bodyEl.show();
28743     },
28744
28745     /**
28746      * Set the tooltip for the tab.
28747      * @param {String} tooltip The tab's tooltip
28748      */
28749     setTooltip : function(text){
28750         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
28751             this.textEl.dom.qtip = text;
28752             this.textEl.dom.removeAttribute('title');
28753         }else{
28754             this.textEl.dom.title = text;
28755         }
28756     },
28757
28758     onTabClick : function(e){
28759         e.preventDefault();
28760         this.tabPanel.activate(this.id);
28761     },
28762
28763     onTabMouseDown : function(e){
28764         e.preventDefault();
28765         this.tabPanel.activate(this.id);
28766     },
28767
28768     getWidth : function(){
28769         return this.inner.getWidth();
28770     },
28771
28772     setWidth : function(width){
28773         var iwidth = width - this.pnode.getPadding("lr");
28774         this.inner.setWidth(iwidth);
28775         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
28776         this.pnode.setWidth(width);
28777     },
28778
28779     /**
28780      * Show or hide the tab
28781      * @param {Boolean} hidden True to hide or false to show.
28782      */
28783     setHidden : function(hidden){
28784         this.hidden = hidden;
28785         this.pnode.setStyle("display", hidden ? "none" : "");
28786     },
28787
28788     /**
28789      * Returns true if this tab is "hidden"
28790      * @return {Boolean}
28791      */
28792     isHidden : function(){
28793         return this.hidden;
28794     },
28795
28796     /**
28797      * Returns the text for this tab
28798      * @return {String}
28799      */
28800     getText : function(){
28801         return this.text;
28802     },
28803
28804     autoSize : function(){
28805         //this.el.beginMeasure();
28806         this.textEl.setWidth(1);
28807         /*
28808          *  #2804 [new] Tabs in Roojs
28809          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
28810          */
28811         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
28812         //this.el.endMeasure();
28813     },
28814
28815     /**
28816      * Sets the text for the tab (Note: this also sets the tooltip text)
28817      * @param {String} text The tab's text and tooltip
28818      */
28819     setText : function(text){
28820         this.text = text;
28821         this.textEl.update(text);
28822         this.setTooltip(text);
28823         if(!this.tabPanel.resizeTabs){
28824             this.autoSize();
28825         }
28826     },
28827     /**
28828      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
28829      */
28830     activate : function(){
28831         this.tabPanel.activate(this.id);
28832     },
28833
28834     /**
28835      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
28836      */
28837     disable : function(){
28838         if(this.tabPanel.active != this){
28839             this.disabled = true;
28840             this.pnode.addClass("disabled");
28841         }
28842     },
28843
28844     /**
28845      * Enables this TabPanelItem if it was previously disabled.
28846      */
28847     enable : function(){
28848         this.disabled = false;
28849         this.pnode.removeClass("disabled");
28850     },
28851
28852     /**
28853      * Sets the content for this TabPanelItem.
28854      * @param {String} content The content
28855      * @param {Boolean} loadScripts true to look for and load scripts
28856      */
28857     setContent : function(content, loadScripts){
28858         this.bodyEl.update(content, loadScripts);
28859     },
28860
28861     /**
28862      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
28863      * @return {Roo.UpdateManager} The UpdateManager
28864      */
28865     getUpdateManager : function(){
28866         return this.bodyEl.getUpdateManager();
28867     },
28868
28869     /**
28870      * Set a URL to be used to load the content for this TabPanelItem.
28871      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
28872      * @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)
28873      * @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)
28874      * @return {Roo.UpdateManager} The UpdateManager
28875      */
28876     setUrl : function(url, params, loadOnce){
28877         if(this.refreshDelegate){
28878             this.un('activate', this.refreshDelegate);
28879         }
28880         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
28881         this.on("activate", this.refreshDelegate);
28882         return this.bodyEl.getUpdateManager();
28883     },
28884
28885     /** @private */
28886     _handleRefresh : function(url, params, loadOnce){
28887         if(!loadOnce || !this.loaded){
28888             var updater = this.bodyEl.getUpdateManager();
28889             updater.update(url, params, this._setLoaded.createDelegate(this));
28890         }
28891     },
28892
28893     /**
28894      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
28895      *   Will fail silently if the setUrl method has not been called.
28896      *   This does not activate the panel, just updates its content.
28897      */
28898     refresh : function(){
28899         if(this.refreshDelegate){
28900            this.loaded = false;
28901            this.refreshDelegate();
28902         }
28903     },
28904
28905     /** @private */
28906     _setLoaded : function(){
28907         this.loaded = true;
28908     },
28909
28910     /** @private */
28911     closeClick : function(e){
28912         var o = {};
28913         e.stopEvent();
28914         this.fireEvent("beforeclose", this, o);
28915         if(o.cancel !== true){
28916             this.tabPanel.removeTab(this.id);
28917         }
28918     },
28919     /**
28920      * The text displayed in the tooltip for the close icon.
28921      * @type String
28922      */
28923     closeText : "Close this tab"
28924 });
28925
28926 /** @private */
28927 Roo.TabPanel.prototype.createStrip = function(container){
28928     var strip = document.createElement("div");
28929     strip.className = "x-tabs-wrap";
28930     container.appendChild(strip);
28931     return strip;
28932 };
28933 /** @private */
28934 Roo.TabPanel.prototype.createStripList = function(strip){
28935     // div wrapper for retard IE
28936     // returns the "tr" element.
28937     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
28938         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
28939         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
28940     return strip.firstChild.firstChild.firstChild.firstChild;
28941 };
28942 /** @private */
28943 Roo.TabPanel.prototype.createBody = function(container){
28944     var body = document.createElement("div");
28945     Roo.id(body, "tab-body");
28946     Roo.fly(body).addClass("x-tabs-body");
28947     container.appendChild(body);
28948     return body;
28949 };
28950 /** @private */
28951 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
28952     var body = Roo.getDom(id);
28953     if(!body){
28954         body = document.createElement("div");
28955         body.id = id;
28956     }
28957     Roo.fly(body).addClass("x-tabs-item-body");
28958     bodyEl.insertBefore(body, bodyEl.firstChild);
28959     return body;
28960 };
28961 /** @private */
28962 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
28963     var td = document.createElement("td");
28964     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
28965     //stripEl.appendChild(td);
28966     if(closable){
28967         td.className = "x-tabs-closable";
28968         if(!this.closeTpl){
28969             this.closeTpl = new Roo.Template(
28970                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
28971                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
28972                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
28973             );
28974         }
28975         var el = this.closeTpl.overwrite(td, {"text": text});
28976         var close = el.getElementsByTagName("div")[0];
28977         var inner = el.getElementsByTagName("em")[0];
28978         return {"el": el, "close": close, "inner": inner};
28979     } else {
28980         if(!this.tabTpl){
28981             this.tabTpl = new Roo.Template(
28982                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
28983                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
28984             );
28985         }
28986         var el = this.tabTpl.overwrite(td, {"text": text});
28987         var inner = el.getElementsByTagName("em")[0];
28988         return {"el": el, "inner": inner};
28989     }
28990 };/*
28991  * Based on:
28992  * Ext JS Library 1.1.1
28993  * Copyright(c) 2006-2007, Ext JS, LLC.
28994  *
28995  * Originally Released Under LGPL - original licence link has changed is not relivant.
28996  *
28997  * Fork - LGPL
28998  * <script type="text/javascript">
28999  */
29000
29001 /**
29002  * @class Roo.Button
29003  * @extends Roo.util.Observable
29004  * Simple Button class
29005  * @cfg {String} text The button text
29006  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29007  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29008  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29009  * @cfg {Object} scope The scope of the handler
29010  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29011  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29012  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29013  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29014  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29015  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29016    applies if enableToggle = true)
29017  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29018  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29019   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29020  * @constructor
29021  * Create a new button
29022  * @param {Object} config The config object
29023  */
29024 Roo.Button = function(renderTo, config)
29025 {
29026     if (!config) {
29027         config = renderTo;
29028         renderTo = config.renderTo || false;
29029     }
29030     
29031     Roo.apply(this, config);
29032     this.addEvents({
29033         /**
29034              * @event click
29035              * Fires when this button is clicked
29036              * @param {Button} this
29037              * @param {EventObject} e The click event
29038              */
29039             "click" : true,
29040         /**
29041              * @event toggle
29042              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29043              * @param {Button} this
29044              * @param {Boolean} pressed
29045              */
29046             "toggle" : true,
29047         /**
29048              * @event mouseover
29049              * Fires when the mouse hovers over the button
29050              * @param {Button} this
29051              * @param {Event} e The event object
29052              */
29053         'mouseover' : true,
29054         /**
29055              * @event mouseout
29056              * Fires when the mouse exits the button
29057              * @param {Button} this
29058              * @param {Event} e The event object
29059              */
29060         'mouseout': true,
29061          /**
29062              * @event render
29063              * Fires when the button is rendered
29064              * @param {Button} this
29065              */
29066         'render': true
29067     });
29068     if(this.menu){
29069         this.menu = Roo.menu.MenuMgr.get(this.menu);
29070     }
29071     // register listeners first!!  - so render can be captured..
29072     Roo.util.Observable.call(this);
29073     if(renderTo){
29074         this.render(renderTo);
29075     }
29076     
29077   
29078 };
29079
29080 Roo.extend(Roo.Button, Roo.util.Observable, {
29081     /**
29082      * 
29083      */
29084     
29085     /**
29086      * Read-only. True if this button is hidden
29087      * @type Boolean
29088      */
29089     hidden : false,
29090     /**
29091      * Read-only. True if this button is disabled
29092      * @type Boolean
29093      */
29094     disabled : false,
29095     /**
29096      * Read-only. True if this button is pressed (only if enableToggle = true)
29097      * @type Boolean
29098      */
29099     pressed : false,
29100
29101     /**
29102      * @cfg {Number} tabIndex 
29103      * The DOM tabIndex for this button (defaults to undefined)
29104      */
29105     tabIndex : undefined,
29106
29107     /**
29108      * @cfg {Boolean} enableToggle
29109      * True to enable pressed/not pressed toggling (defaults to false)
29110      */
29111     enableToggle: false,
29112     /**
29113      * @cfg {Mixed} menu
29114      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29115      */
29116     menu : undefined,
29117     /**
29118      * @cfg {String} menuAlign
29119      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29120      */
29121     menuAlign : "tl-bl?",
29122
29123     /**
29124      * @cfg {String} iconCls
29125      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29126      */
29127     iconCls : undefined,
29128     /**
29129      * @cfg {String} type
29130      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29131      */
29132     type : 'button',
29133
29134     // private
29135     menuClassTarget: 'tr',
29136
29137     /**
29138      * @cfg {String} clickEvent
29139      * The type of event to map to the button's event handler (defaults to 'click')
29140      */
29141     clickEvent : 'click',
29142
29143     /**
29144      * @cfg {Boolean} handleMouseEvents
29145      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29146      */
29147     handleMouseEvents : true,
29148
29149     /**
29150      * @cfg {String} tooltipType
29151      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29152      */
29153     tooltipType : 'qtip',
29154
29155     /**
29156      * @cfg {String} cls
29157      * A CSS class to apply to the button's main element.
29158      */
29159     
29160     /**
29161      * @cfg {Roo.Template} template (Optional)
29162      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29163      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29164      * require code modifications if required elements (e.g. a button) aren't present.
29165      */
29166
29167     // private
29168     render : function(renderTo){
29169         var btn;
29170         if(this.hideParent){
29171             this.parentEl = Roo.get(renderTo);
29172         }
29173         if(!this.dhconfig){
29174             if(!this.template){
29175                 if(!Roo.Button.buttonTemplate){
29176                     // hideous table template
29177                     Roo.Button.buttonTemplate = new Roo.Template(
29178                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29179                         '<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>',
29180                         "</tr></tbody></table>");
29181                 }
29182                 this.template = Roo.Button.buttonTemplate;
29183             }
29184             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29185             var btnEl = btn.child("button:first");
29186             btnEl.on('focus', this.onFocus, this);
29187             btnEl.on('blur', this.onBlur, this);
29188             if(this.cls){
29189                 btn.addClass(this.cls);
29190             }
29191             if(this.icon){
29192                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29193             }
29194             if(this.iconCls){
29195                 btnEl.addClass(this.iconCls);
29196                 if(!this.cls){
29197                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29198                 }
29199             }
29200             if(this.tabIndex !== undefined){
29201                 btnEl.dom.tabIndex = this.tabIndex;
29202             }
29203             if(this.tooltip){
29204                 if(typeof this.tooltip == 'object'){
29205                     Roo.QuickTips.tips(Roo.apply({
29206                           target: btnEl.id
29207                     }, this.tooltip));
29208                 } else {
29209                     btnEl.dom[this.tooltipType] = this.tooltip;
29210                 }
29211             }
29212         }else{
29213             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29214         }
29215         this.el = btn;
29216         if(this.id){
29217             this.el.dom.id = this.el.id = this.id;
29218         }
29219         if(this.menu){
29220             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29221             this.menu.on("show", this.onMenuShow, this);
29222             this.menu.on("hide", this.onMenuHide, this);
29223         }
29224         btn.addClass("x-btn");
29225         if(Roo.isIE && !Roo.isIE7){
29226             this.autoWidth.defer(1, this);
29227         }else{
29228             this.autoWidth();
29229         }
29230         if(this.handleMouseEvents){
29231             btn.on("mouseover", this.onMouseOver, this);
29232             btn.on("mouseout", this.onMouseOut, this);
29233             btn.on("mousedown", this.onMouseDown, this);
29234         }
29235         btn.on(this.clickEvent, this.onClick, this);
29236         //btn.on("mouseup", this.onMouseUp, this);
29237         if(this.hidden){
29238             this.hide();
29239         }
29240         if(this.disabled){
29241             this.disable();
29242         }
29243         Roo.ButtonToggleMgr.register(this);
29244         if(this.pressed){
29245             this.el.addClass("x-btn-pressed");
29246         }
29247         if(this.repeat){
29248             var repeater = new Roo.util.ClickRepeater(btn,
29249                 typeof this.repeat == "object" ? this.repeat : {}
29250             );
29251             repeater.on("click", this.onClick,  this);
29252         }
29253         
29254         this.fireEvent('render', this);
29255         
29256     },
29257     /**
29258      * Returns the button's underlying element
29259      * @return {Roo.Element} The element
29260      */
29261     getEl : function(){
29262         return this.el;  
29263     },
29264     
29265     /**
29266      * Destroys this Button and removes any listeners.
29267      */
29268     destroy : function(){
29269         Roo.ButtonToggleMgr.unregister(this);
29270         this.el.removeAllListeners();
29271         this.purgeListeners();
29272         this.el.remove();
29273     },
29274
29275     // private
29276     autoWidth : function(){
29277         if(this.el){
29278             this.el.setWidth("auto");
29279             if(Roo.isIE7 && Roo.isStrict){
29280                 var ib = this.el.child('button');
29281                 if(ib && ib.getWidth() > 20){
29282                     ib.clip();
29283                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29284                 }
29285             }
29286             if(this.minWidth){
29287                 if(this.hidden){
29288                     this.el.beginMeasure();
29289                 }
29290                 if(this.el.getWidth() < this.minWidth){
29291                     this.el.setWidth(this.minWidth);
29292                 }
29293                 if(this.hidden){
29294                     this.el.endMeasure();
29295                 }
29296             }
29297         }
29298     },
29299
29300     /**
29301      * Assigns this button's click handler
29302      * @param {Function} handler The function to call when the button is clicked
29303      * @param {Object} scope (optional) Scope for the function passed in
29304      */
29305     setHandler : function(handler, scope){
29306         this.handler = handler;
29307         this.scope = scope;  
29308     },
29309     
29310     /**
29311      * Sets this button's text
29312      * @param {String} text The button text
29313      */
29314     setText : function(text){
29315         this.text = text;
29316         if(this.el){
29317             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29318         }
29319         this.autoWidth();
29320     },
29321     
29322     /**
29323      * Gets the text for this button
29324      * @return {String} The button text
29325      */
29326     getText : function(){
29327         return this.text;  
29328     },
29329     
29330     /**
29331      * Show this button
29332      */
29333     show: function(){
29334         this.hidden = false;
29335         if(this.el){
29336             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29337         }
29338     },
29339     
29340     /**
29341      * Hide this button
29342      */
29343     hide: function(){
29344         this.hidden = true;
29345         if(this.el){
29346             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29347         }
29348     },
29349     
29350     /**
29351      * Convenience function for boolean show/hide
29352      * @param {Boolean} visible True to show, false to hide
29353      */
29354     setVisible: function(visible){
29355         if(visible) {
29356             this.show();
29357         }else{
29358             this.hide();
29359         }
29360     },
29361     
29362     /**
29363      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29364      * @param {Boolean} state (optional) Force a particular state
29365      */
29366     toggle : function(state){
29367         state = state === undefined ? !this.pressed : state;
29368         if(state != this.pressed){
29369             if(state){
29370                 this.el.addClass("x-btn-pressed");
29371                 this.pressed = true;
29372                 this.fireEvent("toggle", this, true);
29373             }else{
29374                 this.el.removeClass("x-btn-pressed");
29375                 this.pressed = false;
29376                 this.fireEvent("toggle", this, false);
29377             }
29378             if(this.toggleHandler){
29379                 this.toggleHandler.call(this.scope || this, this, state);
29380             }
29381         }
29382     },
29383     
29384     /**
29385      * Focus the button
29386      */
29387     focus : function(){
29388         this.el.child('button:first').focus();
29389     },
29390     
29391     /**
29392      * Disable this button
29393      */
29394     disable : function(){
29395         if(this.el){
29396             this.el.addClass("x-btn-disabled");
29397         }
29398         this.disabled = true;
29399     },
29400     
29401     /**
29402      * Enable this button
29403      */
29404     enable : function(){
29405         if(this.el){
29406             this.el.removeClass("x-btn-disabled");
29407         }
29408         this.disabled = false;
29409     },
29410
29411     /**
29412      * Convenience function for boolean enable/disable
29413      * @param {Boolean} enabled True to enable, false to disable
29414      */
29415     setDisabled : function(v){
29416         this[v !== true ? "enable" : "disable"]();
29417     },
29418
29419     // private
29420     onClick : function(e)
29421     {
29422         if(e){
29423             e.preventDefault();
29424         }
29425         if(e.button != 0){
29426             return;
29427         }
29428         if(!this.disabled){
29429             if(this.enableToggle){
29430                 this.toggle();
29431             }
29432             if(this.menu && !this.menu.isVisible()){
29433                 this.menu.show(this.el, this.menuAlign);
29434             }
29435             this.fireEvent("click", this, e);
29436             if(this.handler){
29437                 this.el.removeClass("x-btn-over");
29438                 this.handler.call(this.scope || this, this, e);
29439             }
29440         }
29441     },
29442     // private
29443     onMouseOver : function(e){
29444         if(!this.disabled){
29445             this.el.addClass("x-btn-over");
29446             this.fireEvent('mouseover', this, e);
29447         }
29448     },
29449     // private
29450     onMouseOut : function(e){
29451         if(!e.within(this.el,  true)){
29452             this.el.removeClass("x-btn-over");
29453             this.fireEvent('mouseout', this, e);
29454         }
29455     },
29456     // private
29457     onFocus : function(e){
29458         if(!this.disabled){
29459             this.el.addClass("x-btn-focus");
29460         }
29461     },
29462     // private
29463     onBlur : function(e){
29464         this.el.removeClass("x-btn-focus");
29465     },
29466     // private
29467     onMouseDown : function(e){
29468         if(!this.disabled && e.button == 0){
29469             this.el.addClass("x-btn-click");
29470             Roo.get(document).on('mouseup', this.onMouseUp, this);
29471         }
29472     },
29473     // private
29474     onMouseUp : function(e){
29475         if(e.button == 0){
29476             this.el.removeClass("x-btn-click");
29477             Roo.get(document).un('mouseup', this.onMouseUp, this);
29478         }
29479     },
29480     // private
29481     onMenuShow : function(e){
29482         this.el.addClass("x-btn-menu-active");
29483     },
29484     // private
29485     onMenuHide : function(e){
29486         this.el.removeClass("x-btn-menu-active");
29487     }   
29488 });
29489
29490 // Private utility class used by Button
29491 Roo.ButtonToggleMgr = function(){
29492    var groups = {};
29493    
29494    function toggleGroup(btn, state){
29495        if(state){
29496            var g = groups[btn.toggleGroup];
29497            for(var i = 0, l = g.length; i < l; i++){
29498                if(g[i] != btn){
29499                    g[i].toggle(false);
29500                }
29501            }
29502        }
29503    }
29504    
29505    return {
29506        register : function(btn){
29507            if(!btn.toggleGroup){
29508                return;
29509            }
29510            var g = groups[btn.toggleGroup];
29511            if(!g){
29512                g = groups[btn.toggleGroup] = [];
29513            }
29514            g.push(btn);
29515            btn.on("toggle", toggleGroup);
29516        },
29517        
29518        unregister : function(btn){
29519            if(!btn.toggleGroup){
29520                return;
29521            }
29522            var g = groups[btn.toggleGroup];
29523            if(g){
29524                g.remove(btn);
29525                btn.un("toggle", toggleGroup);
29526            }
29527        }
29528    };
29529 }();/*
29530  * Based on:
29531  * Ext JS Library 1.1.1
29532  * Copyright(c) 2006-2007, Ext JS, LLC.
29533  *
29534  * Originally Released Under LGPL - original licence link has changed is not relivant.
29535  *
29536  * Fork - LGPL
29537  * <script type="text/javascript">
29538  */
29539  
29540 /**
29541  * @class Roo.SplitButton
29542  * @extends Roo.Button
29543  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29544  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29545  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29546  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29547  * @cfg {String} arrowTooltip The title attribute of the arrow
29548  * @constructor
29549  * Create a new menu button
29550  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29551  * @param {Object} config The config object
29552  */
29553 Roo.SplitButton = function(renderTo, config){
29554     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29555     /**
29556      * @event arrowclick
29557      * Fires when this button's arrow is clicked
29558      * @param {SplitButton} this
29559      * @param {EventObject} e The click event
29560      */
29561     this.addEvents({"arrowclick":true});
29562 };
29563
29564 Roo.extend(Roo.SplitButton, Roo.Button, {
29565     render : function(renderTo){
29566         // this is one sweet looking template!
29567         var tpl = new Roo.Template(
29568             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29569             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29570             '<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>',
29571             "</tbody></table></td><td>",
29572             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29573             '<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>',
29574             "</tbody></table></td></tr></table>"
29575         );
29576         var btn = tpl.append(renderTo, [this.text, this.type], true);
29577         var btnEl = btn.child("button");
29578         if(this.cls){
29579             btn.addClass(this.cls);
29580         }
29581         if(this.icon){
29582             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29583         }
29584         if(this.iconCls){
29585             btnEl.addClass(this.iconCls);
29586             if(!this.cls){
29587                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29588             }
29589         }
29590         this.el = btn;
29591         if(this.handleMouseEvents){
29592             btn.on("mouseover", this.onMouseOver, this);
29593             btn.on("mouseout", this.onMouseOut, this);
29594             btn.on("mousedown", this.onMouseDown, this);
29595             btn.on("mouseup", this.onMouseUp, this);
29596         }
29597         btn.on(this.clickEvent, this.onClick, this);
29598         if(this.tooltip){
29599             if(typeof this.tooltip == 'object'){
29600                 Roo.QuickTips.tips(Roo.apply({
29601                       target: btnEl.id
29602                 }, this.tooltip));
29603             } else {
29604                 btnEl.dom[this.tooltipType] = this.tooltip;
29605             }
29606         }
29607         if(this.arrowTooltip){
29608             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29609         }
29610         if(this.hidden){
29611             this.hide();
29612         }
29613         if(this.disabled){
29614             this.disable();
29615         }
29616         if(this.pressed){
29617             this.el.addClass("x-btn-pressed");
29618         }
29619         if(Roo.isIE && !Roo.isIE7){
29620             this.autoWidth.defer(1, this);
29621         }else{
29622             this.autoWidth();
29623         }
29624         if(this.menu){
29625             this.menu.on("show", this.onMenuShow, this);
29626             this.menu.on("hide", this.onMenuHide, this);
29627         }
29628         this.fireEvent('render', this);
29629     },
29630
29631     // private
29632     autoWidth : function(){
29633         if(this.el){
29634             var tbl = this.el.child("table:first");
29635             var tbl2 = this.el.child("table:last");
29636             this.el.setWidth("auto");
29637             tbl.setWidth("auto");
29638             if(Roo.isIE7 && Roo.isStrict){
29639                 var ib = this.el.child('button:first');
29640                 if(ib && ib.getWidth() > 20){
29641                     ib.clip();
29642                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29643                 }
29644             }
29645             if(this.minWidth){
29646                 if(this.hidden){
29647                     this.el.beginMeasure();
29648                 }
29649                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29650                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29651                 }
29652                 if(this.hidden){
29653                     this.el.endMeasure();
29654                 }
29655             }
29656             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29657         } 
29658     },
29659     /**
29660      * Sets this button's click handler
29661      * @param {Function} handler The function to call when the button is clicked
29662      * @param {Object} scope (optional) Scope for the function passed above
29663      */
29664     setHandler : function(handler, scope){
29665         this.handler = handler;
29666         this.scope = scope;  
29667     },
29668     
29669     /**
29670      * Sets this button's arrow click handler
29671      * @param {Function} handler The function to call when the arrow is clicked
29672      * @param {Object} scope (optional) Scope for the function passed above
29673      */
29674     setArrowHandler : function(handler, scope){
29675         this.arrowHandler = handler;
29676         this.scope = scope;  
29677     },
29678     
29679     /**
29680      * Focus the button
29681      */
29682     focus : function(){
29683         if(this.el){
29684             this.el.child("button:first").focus();
29685         }
29686     },
29687
29688     // private
29689     onClick : function(e){
29690         e.preventDefault();
29691         if(!this.disabled){
29692             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29693                 if(this.menu && !this.menu.isVisible()){
29694                     this.menu.show(this.el, this.menuAlign);
29695                 }
29696                 this.fireEvent("arrowclick", this, e);
29697                 if(this.arrowHandler){
29698                     this.arrowHandler.call(this.scope || this, this, e);
29699                 }
29700             }else{
29701                 this.fireEvent("click", this, e);
29702                 if(this.handler){
29703                     this.handler.call(this.scope || this, this, e);
29704                 }
29705             }
29706         }
29707     },
29708     // private
29709     onMouseDown : function(e){
29710         if(!this.disabled){
29711             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29712         }
29713     },
29714     // private
29715     onMouseUp : function(e){
29716         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29717     }   
29718 });
29719
29720
29721 // backwards compat
29722 Roo.MenuButton = Roo.SplitButton;/*
29723  * Based on:
29724  * Ext JS Library 1.1.1
29725  * Copyright(c) 2006-2007, Ext JS, LLC.
29726  *
29727  * Originally Released Under LGPL - original licence link has changed is not relivant.
29728  *
29729  * Fork - LGPL
29730  * <script type="text/javascript">
29731  */
29732
29733 /**
29734  * @class Roo.Toolbar
29735  * Basic Toolbar class.
29736  * @constructor
29737  * Creates a new Toolbar
29738  * @param {Object} container The config object
29739  */ 
29740 Roo.Toolbar = function(container, buttons, config)
29741 {
29742     /// old consturctor format still supported..
29743     if(container instanceof Array){ // omit the container for later rendering
29744         buttons = container;
29745         config = buttons;
29746         container = null;
29747     }
29748     if (typeof(container) == 'object' && container.xtype) {
29749         config = container;
29750         container = config.container;
29751         buttons = config.buttons || []; // not really - use items!!
29752     }
29753     var xitems = [];
29754     if (config && config.items) {
29755         xitems = config.items;
29756         delete config.items;
29757     }
29758     Roo.apply(this, config);
29759     this.buttons = buttons;
29760     
29761     if(container){
29762         this.render(container);
29763     }
29764     this.xitems = xitems;
29765     Roo.each(xitems, function(b) {
29766         this.add(b);
29767     }, this);
29768     
29769 };
29770
29771 Roo.Toolbar.prototype = {
29772     /**
29773      * @cfg {Array} items
29774      * array of button configs or elements to add (will be converted to a MixedCollection)
29775      */
29776     
29777     /**
29778      * @cfg {String/HTMLElement/Element} container
29779      * The id or element that will contain the toolbar
29780      */
29781     // private
29782     render : function(ct){
29783         this.el = Roo.get(ct);
29784         if(this.cls){
29785             this.el.addClass(this.cls);
29786         }
29787         // using a table allows for vertical alignment
29788         // 100% width is needed by Safari...
29789         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
29790         this.tr = this.el.child("tr", true);
29791         var autoId = 0;
29792         this.items = new Roo.util.MixedCollection(false, function(o){
29793             return o.id || ("item" + (++autoId));
29794         });
29795         if(this.buttons){
29796             this.add.apply(this, this.buttons);
29797             delete this.buttons;
29798         }
29799     },
29800
29801     /**
29802      * Adds element(s) to the toolbar -- this function takes a variable number of 
29803      * arguments of mixed type and adds them to the toolbar.
29804      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
29805      * <ul>
29806      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
29807      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
29808      * <li>Field: Any form field (equivalent to {@link #addField})</li>
29809      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
29810      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
29811      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
29812      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
29813      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
29814      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
29815      * </ul>
29816      * @param {Mixed} arg2
29817      * @param {Mixed} etc.
29818      */
29819     add : function(){
29820         var a = arguments, l = a.length;
29821         for(var i = 0; i < l; i++){
29822             this._add(a[i]);
29823         }
29824     },
29825     // private..
29826     _add : function(el) {
29827         
29828         if (el.xtype) {
29829             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
29830         }
29831         
29832         if (el.applyTo){ // some kind of form field
29833             return this.addField(el);
29834         } 
29835         if (el.render){ // some kind of Toolbar.Item
29836             return this.addItem(el);
29837         }
29838         if (typeof el == "string"){ // string
29839             if(el == "separator" || el == "-"){
29840                 return this.addSeparator();
29841             }
29842             if (el == " "){
29843                 return this.addSpacer();
29844             }
29845             if(el == "->"){
29846                 return this.addFill();
29847             }
29848             return this.addText(el);
29849             
29850         }
29851         if(el.tagName){ // element
29852             return this.addElement(el);
29853         }
29854         if(typeof el == "object"){ // must be button config?
29855             return this.addButton(el);
29856         }
29857         // and now what?!?!
29858         return false;
29859         
29860     },
29861     
29862     /**
29863      * Add an Xtype element
29864      * @param {Object} xtype Xtype Object
29865      * @return {Object} created Object
29866      */
29867     addxtype : function(e){
29868         return this.add(e);  
29869     },
29870     
29871     /**
29872      * Returns the Element for this toolbar.
29873      * @return {Roo.Element}
29874      */
29875     getEl : function(){
29876         return this.el;  
29877     },
29878     
29879     /**
29880      * Adds a separator
29881      * @return {Roo.Toolbar.Item} The separator item
29882      */
29883     addSeparator : function(){
29884         return this.addItem(new Roo.Toolbar.Separator());
29885     },
29886
29887     /**
29888      * Adds a spacer element
29889      * @return {Roo.Toolbar.Spacer} The spacer item
29890      */
29891     addSpacer : function(){
29892         return this.addItem(new Roo.Toolbar.Spacer());
29893     },
29894
29895     /**
29896      * Adds a fill element that forces subsequent additions to the right side of the toolbar
29897      * @return {Roo.Toolbar.Fill} The fill item
29898      */
29899     addFill : function(){
29900         return this.addItem(new Roo.Toolbar.Fill());
29901     },
29902
29903     /**
29904      * Adds any standard HTML element to the toolbar
29905      * @param {String/HTMLElement/Element} el The element or id of the element to add
29906      * @return {Roo.Toolbar.Item} The element's item
29907      */
29908     addElement : function(el){
29909         return this.addItem(new Roo.Toolbar.Item(el));
29910     },
29911     /**
29912      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
29913      * @type Roo.util.MixedCollection  
29914      */
29915     items : false,
29916      
29917     /**
29918      * Adds any Toolbar.Item or subclass
29919      * @param {Roo.Toolbar.Item} item
29920      * @return {Roo.Toolbar.Item} The item
29921      */
29922     addItem : function(item){
29923         var td = this.nextBlock();
29924         item.render(td);
29925         this.items.add(item);
29926         return item;
29927     },
29928     
29929     /**
29930      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
29931      * @param {Object/Array} config A button config or array of configs
29932      * @return {Roo.Toolbar.Button/Array}
29933      */
29934     addButton : function(config){
29935         if(config instanceof Array){
29936             var buttons = [];
29937             for(var i = 0, len = config.length; i < len; i++) {
29938                 buttons.push(this.addButton(config[i]));
29939             }
29940             return buttons;
29941         }
29942         var b = config;
29943         if(!(config instanceof Roo.Toolbar.Button)){
29944             b = config.split ?
29945                 new Roo.Toolbar.SplitButton(config) :
29946                 new Roo.Toolbar.Button(config);
29947         }
29948         var td = this.nextBlock();
29949         b.render(td);
29950         this.items.add(b);
29951         return b;
29952     },
29953     
29954     /**
29955      * Adds text to the toolbar
29956      * @param {String} text The text to add
29957      * @return {Roo.Toolbar.Item} The element's item
29958      */
29959     addText : function(text){
29960         return this.addItem(new Roo.Toolbar.TextItem(text));
29961     },
29962     
29963     /**
29964      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
29965      * @param {Number} index The index where the item is to be inserted
29966      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
29967      * @return {Roo.Toolbar.Button/Item}
29968      */
29969     insertButton : function(index, item){
29970         if(item instanceof Array){
29971             var buttons = [];
29972             for(var i = 0, len = item.length; i < len; i++) {
29973                buttons.push(this.insertButton(index + i, item[i]));
29974             }
29975             return buttons;
29976         }
29977         if (!(item instanceof Roo.Toolbar.Button)){
29978            item = new Roo.Toolbar.Button(item);
29979         }
29980         var td = document.createElement("td");
29981         this.tr.insertBefore(td, this.tr.childNodes[index]);
29982         item.render(td);
29983         this.items.insert(index, item);
29984         return item;
29985     },
29986     
29987     /**
29988      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
29989      * @param {Object} config
29990      * @return {Roo.Toolbar.Item} The element's item
29991      */
29992     addDom : function(config, returnEl){
29993         var td = this.nextBlock();
29994         Roo.DomHelper.overwrite(td, config);
29995         var ti = new Roo.Toolbar.Item(td.firstChild);
29996         ti.render(td);
29997         this.items.add(ti);
29998         return ti;
29999     },
30000
30001     /**
30002      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30003      * @type Roo.util.MixedCollection  
30004      */
30005     fields : false,
30006     
30007     /**
30008      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30009      * Note: the field should not have been rendered yet. For a field that has already been
30010      * rendered, use {@link #addElement}.
30011      * @param {Roo.form.Field} field
30012      * @return {Roo.ToolbarItem}
30013      */
30014      
30015       
30016     addField : function(field) {
30017         if (!this.fields) {
30018             var autoId = 0;
30019             this.fields = new Roo.util.MixedCollection(false, function(o){
30020                 return o.id || ("item" + (++autoId));
30021             });
30022
30023         }
30024         
30025         var td = this.nextBlock();
30026         field.render(td);
30027         var ti = new Roo.Toolbar.Item(td.firstChild);
30028         ti.render(td);
30029         this.items.add(ti);
30030         this.fields.add(field);
30031         return ti;
30032     },
30033     /**
30034      * Hide the toolbar
30035      * @method hide
30036      */
30037      
30038       
30039     hide : function()
30040     {
30041         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30042         this.el.child('div').hide();
30043     },
30044     /**
30045      * Show the toolbar
30046      * @method show
30047      */
30048     show : function()
30049     {
30050         this.el.child('div').show();
30051     },
30052       
30053     // private
30054     nextBlock : function(){
30055         var td = document.createElement("td");
30056         this.tr.appendChild(td);
30057         return td;
30058     },
30059
30060     // private
30061     destroy : function(){
30062         if(this.items){ // rendered?
30063             Roo.destroy.apply(Roo, this.items.items);
30064         }
30065         if(this.fields){ // rendered?
30066             Roo.destroy.apply(Roo, this.fields.items);
30067         }
30068         Roo.Element.uncache(this.el, this.tr);
30069     }
30070 };
30071
30072 /**
30073  * @class Roo.Toolbar.Item
30074  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30075  * @constructor
30076  * Creates a new Item
30077  * @param {HTMLElement} el 
30078  */
30079 Roo.Toolbar.Item = function(el){
30080     var cfg = {};
30081     if (typeof (el.xtype) != 'undefined') {
30082         cfg = el;
30083         el = cfg.el;
30084     }
30085     
30086     this.el = Roo.getDom(el);
30087     this.id = Roo.id(this.el);
30088     this.hidden = false;
30089     
30090     this.addEvents({
30091          /**
30092              * @event render
30093              * Fires when the button is rendered
30094              * @param {Button} this
30095              */
30096         'render': true
30097     });
30098     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30099 };
30100 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30101 //Roo.Toolbar.Item.prototype = {
30102     
30103     /**
30104      * Get this item's HTML Element
30105      * @return {HTMLElement}
30106      */
30107     getEl : function(){
30108        return this.el;  
30109     },
30110
30111     // private
30112     render : function(td){
30113         
30114          this.td = td;
30115         td.appendChild(this.el);
30116         
30117         this.fireEvent('render', this);
30118     },
30119     
30120     /**
30121      * Removes and destroys this item.
30122      */
30123     destroy : function(){
30124         this.td.parentNode.removeChild(this.td);
30125     },
30126     
30127     /**
30128      * Shows this item.
30129      */
30130     show: function(){
30131         this.hidden = false;
30132         this.td.style.display = "";
30133     },
30134     
30135     /**
30136      * Hides this item.
30137      */
30138     hide: function(){
30139         this.hidden = true;
30140         this.td.style.display = "none";
30141     },
30142     
30143     /**
30144      * Convenience function for boolean show/hide.
30145      * @param {Boolean} visible true to show/false to hide
30146      */
30147     setVisible: function(visible){
30148         if(visible) {
30149             this.show();
30150         }else{
30151             this.hide();
30152         }
30153     },
30154     
30155     /**
30156      * Try to focus this item.
30157      */
30158     focus : function(){
30159         Roo.fly(this.el).focus();
30160     },
30161     
30162     /**
30163      * Disables this item.
30164      */
30165     disable : function(){
30166         Roo.fly(this.td).addClass("x-item-disabled");
30167         this.disabled = true;
30168         this.el.disabled = true;
30169     },
30170     
30171     /**
30172      * Enables this item.
30173      */
30174     enable : function(){
30175         Roo.fly(this.td).removeClass("x-item-disabled");
30176         this.disabled = false;
30177         this.el.disabled = false;
30178     }
30179 });
30180
30181
30182 /**
30183  * @class Roo.Toolbar.Separator
30184  * @extends Roo.Toolbar.Item
30185  * A simple toolbar separator class
30186  * @constructor
30187  * Creates a new Separator
30188  */
30189 Roo.Toolbar.Separator = function(cfg){
30190     
30191     var s = document.createElement("span");
30192     s.className = "ytb-sep";
30193     if (cfg) {
30194         cfg.el = s;
30195     }
30196     
30197     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30198 };
30199 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30200     enable:Roo.emptyFn,
30201     disable:Roo.emptyFn,
30202     focus:Roo.emptyFn
30203 });
30204
30205 /**
30206  * @class Roo.Toolbar.Spacer
30207  * @extends Roo.Toolbar.Item
30208  * A simple element that adds extra horizontal space to a toolbar.
30209  * @constructor
30210  * Creates a new Spacer
30211  */
30212 Roo.Toolbar.Spacer = function(cfg){
30213     var s = document.createElement("div");
30214     s.className = "ytb-spacer";
30215     if (cfg) {
30216         cfg.el = s;
30217     }
30218     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30219 };
30220 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30221     enable:Roo.emptyFn,
30222     disable:Roo.emptyFn,
30223     focus:Roo.emptyFn
30224 });
30225
30226 /**
30227  * @class Roo.Toolbar.Fill
30228  * @extends Roo.Toolbar.Spacer
30229  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30230  * @constructor
30231  * Creates a new Spacer
30232  */
30233 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30234     // private
30235     render : function(td){
30236         td.style.width = '100%';
30237         Roo.Toolbar.Fill.superclass.render.call(this, td);
30238     }
30239 });
30240
30241 /**
30242  * @class Roo.Toolbar.TextItem
30243  * @extends Roo.Toolbar.Item
30244  * A simple class that renders text directly into a toolbar.
30245  * @constructor
30246  * Creates a new TextItem
30247  * @param {String} text
30248  */
30249 Roo.Toolbar.TextItem = function(cfg){
30250     var  text = cfg || "";
30251     if (typeof(cfg) == 'object') {
30252         text = cfg.text || "";
30253     }  else {
30254         cfg = null;
30255     }
30256     var s = document.createElement("span");
30257     s.className = "ytb-text";
30258     s.innerHTML = text;
30259     if (cfg) {
30260         cfg.el  = s;
30261     }
30262     
30263     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30264 };
30265 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30266     
30267      
30268     enable:Roo.emptyFn,
30269     disable:Roo.emptyFn,
30270     focus:Roo.emptyFn
30271 });
30272
30273 /**
30274  * @class Roo.Toolbar.Button
30275  * @extends Roo.Button
30276  * A button that renders into a toolbar.
30277  * @constructor
30278  * Creates a new Button
30279  * @param {Object} config A standard {@link Roo.Button} config object
30280  */
30281 Roo.Toolbar.Button = function(config){
30282     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30283 };
30284 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30285     render : function(td){
30286         this.td = td;
30287         Roo.Toolbar.Button.superclass.render.call(this, td);
30288     },
30289     
30290     /**
30291      * Removes and destroys this button
30292      */
30293     destroy : function(){
30294         Roo.Toolbar.Button.superclass.destroy.call(this);
30295         this.td.parentNode.removeChild(this.td);
30296     },
30297     
30298     /**
30299      * Shows this button
30300      */
30301     show: function(){
30302         this.hidden = false;
30303         this.td.style.display = "";
30304     },
30305     
30306     /**
30307      * Hides this button
30308      */
30309     hide: function(){
30310         this.hidden = true;
30311         this.td.style.display = "none";
30312     },
30313
30314     /**
30315      * Disables this item
30316      */
30317     disable : function(){
30318         Roo.fly(this.td).addClass("x-item-disabled");
30319         this.disabled = true;
30320     },
30321
30322     /**
30323      * Enables this item
30324      */
30325     enable : function(){
30326         Roo.fly(this.td).removeClass("x-item-disabled");
30327         this.disabled = false;
30328     }
30329 });
30330 // backwards compat
30331 Roo.ToolbarButton = Roo.Toolbar.Button;
30332
30333 /**
30334  * @class Roo.Toolbar.SplitButton
30335  * @extends Roo.SplitButton
30336  * A menu button that renders into a toolbar.
30337  * @constructor
30338  * Creates a new SplitButton
30339  * @param {Object} config A standard {@link Roo.SplitButton} config object
30340  */
30341 Roo.Toolbar.SplitButton = function(config){
30342     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30343 };
30344 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30345     render : function(td){
30346         this.td = td;
30347         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30348     },
30349     
30350     /**
30351      * Removes and destroys this button
30352      */
30353     destroy : function(){
30354         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30355         this.td.parentNode.removeChild(this.td);
30356     },
30357     
30358     /**
30359      * Shows this button
30360      */
30361     show: function(){
30362         this.hidden = false;
30363         this.td.style.display = "";
30364     },
30365     
30366     /**
30367      * Hides this button
30368      */
30369     hide: function(){
30370         this.hidden = true;
30371         this.td.style.display = "none";
30372     }
30373 });
30374
30375 // backwards compat
30376 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30377  * Based on:
30378  * Ext JS Library 1.1.1
30379  * Copyright(c) 2006-2007, Ext JS, LLC.
30380  *
30381  * Originally Released Under LGPL - original licence link has changed is not relivant.
30382  *
30383  * Fork - LGPL
30384  * <script type="text/javascript">
30385  */
30386  
30387 /**
30388  * @class Roo.PagingToolbar
30389  * @extends Roo.Toolbar
30390  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30391  * @constructor
30392  * Create a new PagingToolbar
30393  * @param {Object} config The config object
30394  */
30395 Roo.PagingToolbar = function(el, ds, config)
30396 {
30397     // old args format still supported... - xtype is prefered..
30398     if (typeof(el) == 'object' && el.xtype) {
30399         // created from xtype...
30400         config = el;
30401         ds = el.dataSource;
30402         el = config.container;
30403     }
30404     var items = [];
30405     if (config.items) {
30406         items = config.items;
30407         config.items = [];
30408     }
30409     
30410     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30411     this.ds = ds;
30412     this.cursor = 0;
30413     this.renderButtons(this.el);
30414     this.bind(ds);
30415     
30416     // supprot items array.
30417    
30418     Roo.each(items, function(e) {
30419         this.add(Roo.factory(e));
30420     },this);
30421     
30422 };
30423
30424 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30425     /**
30426      * @cfg {Roo.data.Store} dataSource
30427      * The underlying data store providing the paged data
30428      */
30429     /**
30430      * @cfg {String/HTMLElement/Element} container
30431      * container The id or element that will contain the toolbar
30432      */
30433     /**
30434      * @cfg {Boolean} displayInfo
30435      * True to display the displayMsg (defaults to false)
30436      */
30437     /**
30438      * @cfg {Number} pageSize
30439      * The number of records to display per page (defaults to 20)
30440      */
30441     pageSize: 20,
30442     /**
30443      * @cfg {String} displayMsg
30444      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30445      */
30446     displayMsg : 'Displaying {0} - {1} of {2}',
30447     /**
30448      * @cfg {String} emptyMsg
30449      * The message to display when no records are found (defaults to "No data to display")
30450      */
30451     emptyMsg : 'No data to display',
30452     /**
30453      * Customizable piece of the default paging text (defaults to "Page")
30454      * @type String
30455      */
30456     beforePageText : "Page",
30457     /**
30458      * Customizable piece of the default paging text (defaults to "of %0")
30459      * @type String
30460      */
30461     afterPageText : "of {0}",
30462     /**
30463      * Customizable piece of the default paging text (defaults to "First Page")
30464      * @type String
30465      */
30466     firstText : "First Page",
30467     /**
30468      * Customizable piece of the default paging text (defaults to "Previous Page")
30469      * @type String
30470      */
30471     prevText : "Previous Page",
30472     /**
30473      * Customizable piece of the default paging text (defaults to "Next Page")
30474      * @type String
30475      */
30476     nextText : "Next Page",
30477     /**
30478      * Customizable piece of the default paging text (defaults to "Last Page")
30479      * @type String
30480      */
30481     lastText : "Last Page",
30482     /**
30483      * Customizable piece of the default paging text (defaults to "Refresh")
30484      * @type String
30485      */
30486     refreshText : "Refresh",
30487
30488     // private
30489     renderButtons : function(el){
30490         Roo.PagingToolbar.superclass.render.call(this, el);
30491         this.first = this.addButton({
30492             tooltip: this.firstText,
30493             cls: "x-btn-icon x-grid-page-first",
30494             disabled: true,
30495             handler: this.onClick.createDelegate(this, ["first"])
30496         });
30497         this.prev = this.addButton({
30498             tooltip: this.prevText,
30499             cls: "x-btn-icon x-grid-page-prev",
30500             disabled: true,
30501             handler: this.onClick.createDelegate(this, ["prev"])
30502         });
30503         //this.addSeparator();
30504         this.add(this.beforePageText);
30505         this.field = Roo.get(this.addDom({
30506            tag: "input",
30507            type: "text",
30508            size: "3",
30509            value: "1",
30510            cls: "x-grid-page-number"
30511         }).el);
30512         this.field.on("keydown", this.onPagingKeydown, this);
30513         this.field.on("focus", function(){this.dom.select();});
30514         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30515         this.field.setHeight(18);
30516         //this.addSeparator();
30517         this.next = this.addButton({
30518             tooltip: this.nextText,
30519             cls: "x-btn-icon x-grid-page-next",
30520             disabled: true,
30521             handler: this.onClick.createDelegate(this, ["next"])
30522         });
30523         this.last = this.addButton({
30524             tooltip: this.lastText,
30525             cls: "x-btn-icon x-grid-page-last",
30526             disabled: true,
30527             handler: this.onClick.createDelegate(this, ["last"])
30528         });
30529         //this.addSeparator();
30530         this.loading = this.addButton({
30531             tooltip: this.refreshText,
30532             cls: "x-btn-icon x-grid-loading",
30533             handler: this.onClick.createDelegate(this, ["refresh"])
30534         });
30535
30536         if(this.displayInfo){
30537             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30538         }
30539     },
30540
30541     // private
30542     updateInfo : function(){
30543         if(this.displayEl){
30544             var count = this.ds.getCount();
30545             var msg = count == 0 ?
30546                 this.emptyMsg :
30547                 String.format(
30548                     this.displayMsg,
30549                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30550                 );
30551             this.displayEl.update(msg);
30552         }
30553     },
30554
30555     // private
30556     onLoad : function(ds, r, o){
30557        this.cursor = o.params ? o.params.start : 0;
30558        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30559
30560        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30561        this.field.dom.value = ap;
30562        this.first.setDisabled(ap == 1);
30563        this.prev.setDisabled(ap == 1);
30564        this.next.setDisabled(ap == ps);
30565        this.last.setDisabled(ap == ps);
30566        this.loading.enable();
30567        this.updateInfo();
30568     },
30569
30570     // private
30571     getPageData : function(){
30572         var total = this.ds.getTotalCount();
30573         return {
30574             total : total,
30575             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30576             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30577         };
30578     },
30579
30580     // private
30581     onLoadError : function(){
30582         this.loading.enable();
30583     },
30584
30585     // private
30586     onPagingKeydown : function(e){
30587         var k = e.getKey();
30588         var d = this.getPageData();
30589         if(k == e.RETURN){
30590             var v = this.field.dom.value, pageNum;
30591             if(!v || isNaN(pageNum = parseInt(v, 10))){
30592                 this.field.dom.value = d.activePage;
30593                 return;
30594             }
30595             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30596             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30597             e.stopEvent();
30598         }
30599         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))
30600         {
30601           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30602           this.field.dom.value = pageNum;
30603           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30604           e.stopEvent();
30605         }
30606         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30607         {
30608           var v = this.field.dom.value, pageNum; 
30609           var increment = (e.shiftKey) ? 10 : 1;
30610           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30611             increment *= -1;
30612           }
30613           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30614             this.field.dom.value = d.activePage;
30615             return;
30616           }
30617           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30618           {
30619             this.field.dom.value = parseInt(v, 10) + increment;
30620             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30621             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30622           }
30623           e.stopEvent();
30624         }
30625     },
30626
30627     // private
30628     beforeLoad : function(){
30629         if(this.loading){
30630             this.loading.disable();
30631         }
30632     },
30633
30634     // private
30635     onClick : function(which){
30636         var ds = this.ds;
30637         switch(which){
30638             case "first":
30639                 ds.load({params:{start: 0, limit: this.pageSize}});
30640             break;
30641             case "prev":
30642                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30643             break;
30644             case "next":
30645                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30646             break;
30647             case "last":
30648                 var total = ds.getTotalCount();
30649                 var extra = total % this.pageSize;
30650                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30651                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30652             break;
30653             case "refresh":
30654                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30655             break;
30656         }
30657     },
30658
30659     /**
30660      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30661      * @param {Roo.data.Store} store The data store to unbind
30662      */
30663     unbind : function(ds){
30664         ds.un("beforeload", this.beforeLoad, this);
30665         ds.un("load", this.onLoad, this);
30666         ds.un("loadexception", this.onLoadError, this);
30667         ds.un("remove", this.updateInfo, this);
30668         ds.un("add", this.updateInfo, this);
30669         this.ds = undefined;
30670     },
30671
30672     /**
30673      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30674      * @param {Roo.data.Store} store The data store to bind
30675      */
30676     bind : function(ds){
30677         ds.on("beforeload", this.beforeLoad, this);
30678         ds.on("load", this.onLoad, this);
30679         ds.on("loadexception", this.onLoadError, this);
30680         ds.on("remove", this.updateInfo, this);
30681         ds.on("add", this.updateInfo, this);
30682         this.ds = ds;
30683     }
30684 });/*
30685  * Based on:
30686  * Ext JS Library 1.1.1
30687  * Copyright(c) 2006-2007, Ext JS, LLC.
30688  *
30689  * Originally Released Under LGPL - original licence link has changed is not relivant.
30690  *
30691  * Fork - LGPL
30692  * <script type="text/javascript">
30693  */
30694
30695 /**
30696  * @class Roo.Resizable
30697  * @extends Roo.util.Observable
30698  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30699  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30700  * 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
30701  * the element will be wrapped for you automatically.</p>
30702  * <p>Here is the list of valid resize handles:</p>
30703  * <pre>
30704 Value   Description
30705 ------  -------------------
30706  'n'     north
30707  's'     south
30708  'e'     east
30709  'w'     west
30710  'nw'    northwest
30711  'sw'    southwest
30712  'se'    southeast
30713  'ne'    northeast
30714  'hd'    horizontal drag
30715  'all'   all
30716 </pre>
30717  * <p>Here's an example showing the creation of a typical Resizable:</p>
30718  * <pre><code>
30719 var resizer = new Roo.Resizable("element-id", {
30720     handles: 'all',
30721     minWidth: 200,
30722     minHeight: 100,
30723     maxWidth: 500,
30724     maxHeight: 400,
30725     pinned: true
30726 });
30727 resizer.on("resize", myHandler);
30728 </code></pre>
30729  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30730  * resizer.east.setDisplayed(false);</p>
30731  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30732  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30733  * resize operation's new size (defaults to [0, 0])
30734  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30735  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30736  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30737  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30738  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30739  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30740  * @cfg {Number} width The width of the element in pixels (defaults to null)
30741  * @cfg {Number} height The height of the element in pixels (defaults to null)
30742  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30743  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30744  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30745  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
30746  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
30747  * in favor of the handles config option (defaults to false)
30748  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
30749  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
30750  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
30751  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
30752  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
30753  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
30754  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
30755  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
30756  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
30757  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
30758  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
30759  * @constructor
30760  * Create a new resizable component
30761  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
30762  * @param {Object} config configuration options
30763   */
30764 Roo.Resizable = function(el, config)
30765 {
30766     this.el = Roo.get(el);
30767
30768     if(config && config.wrap){
30769         config.resizeChild = this.el;
30770         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
30771         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
30772         this.el.setStyle("overflow", "hidden");
30773         this.el.setPositioning(config.resizeChild.getPositioning());
30774         config.resizeChild.clearPositioning();
30775         if(!config.width || !config.height){
30776             var csize = config.resizeChild.getSize();
30777             this.el.setSize(csize.width, csize.height);
30778         }
30779         if(config.pinned && !config.adjustments){
30780             config.adjustments = "auto";
30781         }
30782     }
30783
30784     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
30785     this.proxy.unselectable();
30786     this.proxy.enableDisplayMode('block');
30787
30788     Roo.apply(this, config);
30789
30790     if(this.pinned){
30791         this.disableTrackOver = true;
30792         this.el.addClass("x-resizable-pinned");
30793     }
30794     // if the element isn't positioned, make it relative
30795     var position = this.el.getStyle("position");
30796     if(position != "absolute" && position != "fixed"){
30797         this.el.setStyle("position", "relative");
30798     }
30799     if(!this.handles){ // no handles passed, must be legacy style
30800         this.handles = 's,e,se';
30801         if(this.multiDirectional){
30802             this.handles += ',n,w';
30803         }
30804     }
30805     if(this.handles == "all"){
30806         this.handles = "n s e w ne nw se sw";
30807     }
30808     var hs = this.handles.split(/\s*?[,;]\s*?| /);
30809     var ps = Roo.Resizable.positions;
30810     for(var i = 0, len = hs.length; i < len; i++){
30811         if(hs[i] && ps[hs[i]]){
30812             var pos = ps[hs[i]];
30813             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
30814         }
30815     }
30816     // legacy
30817     this.corner = this.southeast;
30818     
30819     // updateBox = the box can move..
30820     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
30821         this.updateBox = true;
30822     }
30823
30824     this.activeHandle = null;
30825
30826     if(this.resizeChild){
30827         if(typeof this.resizeChild == "boolean"){
30828             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
30829         }else{
30830             this.resizeChild = Roo.get(this.resizeChild, true);
30831         }
30832     }
30833     
30834     if(this.adjustments == "auto"){
30835         var rc = this.resizeChild;
30836         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
30837         if(rc && (hw || hn)){
30838             rc.position("relative");
30839             rc.setLeft(hw ? hw.el.getWidth() : 0);
30840             rc.setTop(hn ? hn.el.getHeight() : 0);
30841         }
30842         this.adjustments = [
30843             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
30844             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
30845         ];
30846     }
30847
30848     if(this.draggable){
30849         this.dd = this.dynamic ?
30850             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
30851         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
30852     }
30853
30854     // public events
30855     this.addEvents({
30856         /**
30857          * @event beforeresize
30858          * Fired before resize is allowed. Set enabled to false to cancel resize.
30859          * @param {Roo.Resizable} this
30860          * @param {Roo.EventObject} e The mousedown event
30861          */
30862         "beforeresize" : true,
30863         /**
30864          * @event resizing
30865          * Fired a resizing.
30866          * @param {Roo.Resizable} this
30867          * @param {Number} x The new x position
30868          * @param {Number} y The new y position
30869          * @param {Number} w The new w width
30870          * @param {Number} h The new h hight
30871          * @param {Roo.EventObject} e The mouseup event
30872          */
30873         "resizing" : true,
30874         /**
30875          * @event resize
30876          * Fired after a resize.
30877          * @param {Roo.Resizable} this
30878          * @param {Number} width The new width
30879          * @param {Number} height The new height
30880          * @param {Roo.EventObject} e The mouseup event
30881          */
30882         "resize" : true
30883     });
30884
30885     if(this.width !== null && this.height !== null){
30886         this.resizeTo(this.width, this.height);
30887     }else{
30888         this.updateChildSize();
30889     }
30890     if(Roo.isIE){
30891         this.el.dom.style.zoom = 1;
30892     }
30893     Roo.Resizable.superclass.constructor.call(this);
30894 };
30895
30896 Roo.extend(Roo.Resizable, Roo.util.Observable, {
30897         resizeChild : false,
30898         adjustments : [0, 0],
30899         minWidth : 5,
30900         minHeight : 5,
30901         maxWidth : 10000,
30902         maxHeight : 10000,
30903         enabled : true,
30904         animate : false,
30905         duration : .35,
30906         dynamic : false,
30907         handles : false,
30908         multiDirectional : false,
30909         disableTrackOver : false,
30910         easing : 'easeOutStrong',
30911         widthIncrement : 0,
30912         heightIncrement : 0,
30913         pinned : false,
30914         width : null,
30915         height : null,
30916         preserveRatio : false,
30917         transparent: false,
30918         minX: 0,
30919         minY: 0,
30920         draggable: false,
30921
30922         /**
30923          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
30924          */
30925         constrainTo: undefined,
30926         /**
30927          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
30928          */
30929         resizeRegion: undefined,
30930
30931
30932     /**
30933      * Perform a manual resize
30934      * @param {Number} width
30935      * @param {Number} height
30936      */
30937     resizeTo : function(width, height){
30938         this.el.setSize(width, height);
30939         this.updateChildSize();
30940         this.fireEvent("resize", this, width, height, null);
30941     },
30942
30943     // private
30944     startSizing : function(e, handle){
30945         this.fireEvent("beforeresize", this, e);
30946         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
30947
30948             if(!this.overlay){
30949                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
30950                 this.overlay.unselectable();
30951                 this.overlay.enableDisplayMode("block");
30952                 this.overlay.on("mousemove", this.onMouseMove, this);
30953                 this.overlay.on("mouseup", this.onMouseUp, this);
30954             }
30955             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
30956
30957             this.resizing = true;
30958             this.startBox = this.el.getBox();
30959             this.startPoint = e.getXY();
30960             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
30961                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
30962
30963             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30964             this.overlay.show();
30965
30966             if(this.constrainTo) {
30967                 var ct = Roo.get(this.constrainTo);
30968                 this.resizeRegion = ct.getRegion().adjust(
30969                     ct.getFrameWidth('t'),
30970                     ct.getFrameWidth('l'),
30971                     -ct.getFrameWidth('b'),
30972                     -ct.getFrameWidth('r')
30973                 );
30974             }
30975
30976             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
30977             this.proxy.show();
30978             this.proxy.setBox(this.startBox);
30979             if(!this.dynamic){
30980                 this.proxy.setStyle('visibility', 'visible');
30981             }
30982         }
30983     },
30984
30985     // private
30986     onMouseDown : function(handle, e){
30987         if(this.enabled){
30988             e.stopEvent();
30989             this.activeHandle = handle;
30990             this.startSizing(e, handle);
30991         }
30992     },
30993
30994     // private
30995     onMouseUp : function(e){
30996         var size = this.resizeElement();
30997         this.resizing = false;
30998         this.handleOut();
30999         this.overlay.hide();
31000         this.proxy.hide();
31001         this.fireEvent("resize", this, size.width, size.height, e);
31002     },
31003
31004     // private
31005     updateChildSize : function(){
31006         
31007         if(this.resizeChild){
31008             var el = this.el;
31009             var child = this.resizeChild;
31010             var adj = this.adjustments;
31011             if(el.dom.offsetWidth){
31012                 var b = el.getSize(true);
31013                 child.setSize(b.width+adj[0], b.height+adj[1]);
31014             }
31015             // Second call here for IE
31016             // The first call enables instant resizing and
31017             // the second call corrects scroll bars if they
31018             // exist
31019             if(Roo.isIE){
31020                 setTimeout(function(){
31021                     if(el.dom.offsetWidth){
31022                         var b = el.getSize(true);
31023                         child.setSize(b.width+adj[0], b.height+adj[1]);
31024                     }
31025                 }, 10);
31026             }
31027         }
31028     },
31029
31030     // private
31031     snap : function(value, inc, min){
31032         if(!inc || !value) {
31033             return value;
31034         }
31035         var newValue = value;
31036         var m = value % inc;
31037         if(m > 0){
31038             if(m > (inc/2)){
31039                 newValue = value + (inc-m);
31040             }else{
31041                 newValue = value - m;
31042             }
31043         }
31044         return Math.max(min, newValue);
31045     },
31046
31047     // private
31048     resizeElement : function(){
31049         var box = this.proxy.getBox();
31050         if(this.updateBox){
31051             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31052         }else{
31053             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31054         }
31055         this.updateChildSize();
31056         if(!this.dynamic){
31057             this.proxy.hide();
31058         }
31059         return box;
31060     },
31061
31062     // private
31063     constrain : function(v, diff, m, mx){
31064         if(v - diff < m){
31065             diff = v - m;
31066         }else if(v - diff > mx){
31067             diff = mx - v;
31068         }
31069         return diff;
31070     },
31071
31072     // private
31073     onMouseMove : function(e){
31074         
31075         if(this.enabled){
31076             try{// try catch so if something goes wrong the user doesn't get hung
31077
31078             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31079                 return;
31080             }
31081
31082             //var curXY = this.startPoint;
31083             var curSize = this.curSize || this.startBox;
31084             var x = this.startBox.x, y = this.startBox.y;
31085             var ox = x, oy = y;
31086             var w = curSize.width, h = curSize.height;
31087             var ow = w, oh = h;
31088             var mw = this.minWidth, mh = this.minHeight;
31089             var mxw = this.maxWidth, mxh = this.maxHeight;
31090             var wi = this.widthIncrement;
31091             var hi = this.heightIncrement;
31092
31093             var eventXY = e.getXY();
31094             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31095             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31096
31097             var pos = this.activeHandle.position;
31098
31099             switch(pos){
31100                 case "east":
31101                     w += diffX;
31102                     w = Math.min(Math.max(mw, w), mxw);
31103                     break;
31104              
31105                 case "south":
31106                     h += diffY;
31107                     h = Math.min(Math.max(mh, h), mxh);
31108                     break;
31109                 case "southeast":
31110                     w += diffX;
31111                     h += diffY;
31112                     w = Math.min(Math.max(mw, w), mxw);
31113                     h = Math.min(Math.max(mh, h), mxh);
31114                     break;
31115                 case "north":
31116                     diffY = this.constrain(h, diffY, mh, mxh);
31117                     y += diffY;
31118                     h -= diffY;
31119                     break;
31120                 case "hdrag":
31121                     
31122                     if (wi) {
31123                         var adiffX = Math.abs(diffX);
31124                         var sub = (adiffX % wi); // how much 
31125                         if (sub > (wi/2)) { // far enough to snap
31126                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31127                         } else {
31128                             // remove difference.. 
31129                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31130                         }
31131                     }
31132                     x += diffX;
31133                     x = Math.max(this.minX, x);
31134                     break;
31135                 case "west":
31136                     diffX = this.constrain(w, diffX, mw, mxw);
31137                     x += diffX;
31138                     w -= diffX;
31139                     break;
31140                 case "northeast":
31141                     w += diffX;
31142                     w = Math.min(Math.max(mw, w), mxw);
31143                     diffY = this.constrain(h, diffY, mh, mxh);
31144                     y += diffY;
31145                     h -= diffY;
31146                     break;
31147                 case "northwest":
31148                     diffX = this.constrain(w, diffX, mw, mxw);
31149                     diffY = this.constrain(h, diffY, mh, mxh);
31150                     y += diffY;
31151                     h -= diffY;
31152                     x += diffX;
31153                     w -= diffX;
31154                     break;
31155                case "southwest":
31156                     diffX = this.constrain(w, diffX, mw, mxw);
31157                     h += diffY;
31158                     h = Math.min(Math.max(mh, h), mxh);
31159                     x += diffX;
31160                     w -= diffX;
31161                     break;
31162             }
31163
31164             var sw = this.snap(w, wi, mw);
31165             var sh = this.snap(h, hi, mh);
31166             if(sw != w || sh != h){
31167                 switch(pos){
31168                     case "northeast":
31169                         y -= sh - h;
31170                     break;
31171                     case "north":
31172                         y -= sh - h;
31173                         break;
31174                     case "southwest":
31175                         x -= sw - w;
31176                     break;
31177                     case "west":
31178                         x -= sw - w;
31179                         break;
31180                     case "northwest":
31181                         x -= sw - w;
31182                         y -= sh - h;
31183                     break;
31184                 }
31185                 w = sw;
31186                 h = sh;
31187             }
31188
31189             if(this.preserveRatio){
31190                 switch(pos){
31191                     case "southeast":
31192                     case "east":
31193                         h = oh * (w/ow);
31194                         h = Math.min(Math.max(mh, h), mxh);
31195                         w = ow * (h/oh);
31196                        break;
31197                     case "south":
31198                         w = ow * (h/oh);
31199                         w = Math.min(Math.max(mw, w), mxw);
31200                         h = oh * (w/ow);
31201                         break;
31202                     case "northeast":
31203                         w = ow * (h/oh);
31204                         w = Math.min(Math.max(mw, w), mxw);
31205                         h = oh * (w/ow);
31206                     break;
31207                     case "north":
31208                         var tw = w;
31209                         w = ow * (h/oh);
31210                         w = Math.min(Math.max(mw, w), mxw);
31211                         h = oh * (w/ow);
31212                         x += (tw - w) / 2;
31213                         break;
31214                     case "southwest":
31215                         h = oh * (w/ow);
31216                         h = Math.min(Math.max(mh, h), mxh);
31217                         var tw = w;
31218                         w = ow * (h/oh);
31219                         x += tw - w;
31220                         break;
31221                     case "west":
31222                         var th = h;
31223                         h = oh * (w/ow);
31224                         h = Math.min(Math.max(mh, h), mxh);
31225                         y += (th - h) / 2;
31226                         var tw = w;
31227                         w = ow * (h/oh);
31228                         x += tw - w;
31229                        break;
31230                     case "northwest":
31231                         var tw = w;
31232                         var th = h;
31233                         h = oh * (w/ow);
31234                         h = Math.min(Math.max(mh, h), mxh);
31235                         w = ow * (h/oh);
31236                         y += th - h;
31237                         x += tw - w;
31238                        break;
31239
31240                 }
31241             }
31242             if (pos == 'hdrag') {
31243                 w = ow;
31244             }
31245             this.proxy.setBounds(x, y, w, h);
31246             if(this.dynamic){
31247                 this.resizeElement();
31248             }
31249             }catch(e){}
31250         }
31251         this.fireEvent("resizing", this, x, y, w, h, e);
31252     },
31253
31254     // private
31255     handleOver : function(){
31256         if(this.enabled){
31257             this.el.addClass("x-resizable-over");
31258         }
31259     },
31260
31261     // private
31262     handleOut : function(){
31263         if(!this.resizing){
31264             this.el.removeClass("x-resizable-over");
31265         }
31266     },
31267
31268     /**
31269      * Returns the element this component is bound to.
31270      * @return {Roo.Element}
31271      */
31272     getEl : function(){
31273         return this.el;
31274     },
31275
31276     /**
31277      * Returns the resizeChild element (or null).
31278      * @return {Roo.Element}
31279      */
31280     getResizeChild : function(){
31281         return this.resizeChild;
31282     },
31283     groupHandler : function()
31284     {
31285         
31286     },
31287     /**
31288      * Destroys this resizable. If the element was wrapped and
31289      * removeEl is not true then the element remains.
31290      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31291      */
31292     destroy : function(removeEl){
31293         this.proxy.remove();
31294         if(this.overlay){
31295             this.overlay.removeAllListeners();
31296             this.overlay.remove();
31297         }
31298         var ps = Roo.Resizable.positions;
31299         for(var k in ps){
31300             if(typeof ps[k] != "function" && this[ps[k]]){
31301                 var h = this[ps[k]];
31302                 h.el.removeAllListeners();
31303                 h.el.remove();
31304             }
31305         }
31306         if(removeEl){
31307             this.el.update("");
31308             this.el.remove();
31309         }
31310     }
31311 });
31312
31313 // private
31314 // hash to map config positions to true positions
31315 Roo.Resizable.positions = {
31316     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31317     hd: "hdrag"
31318 };
31319
31320 // private
31321 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31322     if(!this.tpl){
31323         // only initialize the template if resizable is used
31324         var tpl = Roo.DomHelper.createTemplate(
31325             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31326         );
31327         tpl.compile();
31328         Roo.Resizable.Handle.prototype.tpl = tpl;
31329     }
31330     this.position = pos;
31331     this.rz = rz;
31332     // show north drag fro topdra
31333     var handlepos = pos == 'hdrag' ? 'north' : pos;
31334     
31335     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31336     if (pos == 'hdrag') {
31337         this.el.setStyle('cursor', 'pointer');
31338     }
31339     this.el.unselectable();
31340     if(transparent){
31341         this.el.setOpacity(0);
31342     }
31343     this.el.on("mousedown", this.onMouseDown, this);
31344     if(!disableTrackOver){
31345         this.el.on("mouseover", this.onMouseOver, this);
31346         this.el.on("mouseout", this.onMouseOut, this);
31347     }
31348 };
31349
31350 // private
31351 Roo.Resizable.Handle.prototype = {
31352     afterResize : function(rz){
31353         Roo.log('after?');
31354         // do nothing
31355     },
31356     // private
31357     onMouseDown : function(e){
31358         this.rz.onMouseDown(this, e);
31359     },
31360     // private
31361     onMouseOver : function(e){
31362         this.rz.handleOver(this, e);
31363     },
31364     // private
31365     onMouseOut : function(e){
31366         this.rz.handleOut(this, e);
31367     }
31368 };/*
31369  * Based on:
31370  * Ext JS Library 1.1.1
31371  * Copyright(c) 2006-2007, Ext JS, LLC.
31372  *
31373  * Originally Released Under LGPL - original licence link has changed is not relivant.
31374  *
31375  * Fork - LGPL
31376  * <script type="text/javascript">
31377  */
31378
31379 /**
31380  * @class Roo.Editor
31381  * @extends Roo.Component
31382  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31383  * @constructor
31384  * Create a new Editor
31385  * @param {Roo.form.Field} field The Field object (or descendant)
31386  * @param {Object} config The config object
31387  */
31388 Roo.Editor = function(field, config){
31389     Roo.Editor.superclass.constructor.call(this, config);
31390     this.field = field;
31391     this.addEvents({
31392         /**
31393              * @event beforestartedit
31394              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31395              * false from the handler of this event.
31396              * @param {Editor} this
31397              * @param {Roo.Element} boundEl The underlying element bound to this editor
31398              * @param {Mixed} value The field value being set
31399              */
31400         "beforestartedit" : true,
31401         /**
31402              * @event startedit
31403              * Fires when this editor is displayed
31404              * @param {Roo.Element} boundEl The underlying element bound to this editor
31405              * @param {Mixed} value The starting field value
31406              */
31407         "startedit" : true,
31408         /**
31409              * @event beforecomplete
31410              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31411              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31412              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31413              * event will not fire since no edit actually occurred.
31414              * @param {Editor} this
31415              * @param {Mixed} value The current field value
31416              * @param {Mixed} startValue The original field value
31417              */
31418         "beforecomplete" : true,
31419         /**
31420              * @event complete
31421              * Fires after editing is complete and any changed value has been written to the underlying field.
31422              * @param {Editor} this
31423              * @param {Mixed} value The current field value
31424              * @param {Mixed} startValue The original field value
31425              */
31426         "complete" : true,
31427         /**
31428          * @event specialkey
31429          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31430          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31431          * @param {Roo.form.Field} this
31432          * @param {Roo.EventObject} e The event object
31433          */
31434         "specialkey" : true
31435     });
31436 };
31437
31438 Roo.extend(Roo.Editor, Roo.Component, {
31439     /**
31440      * @cfg {Boolean/String} autosize
31441      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31442      * or "height" to adopt the height only (defaults to false)
31443      */
31444     /**
31445      * @cfg {Boolean} revertInvalid
31446      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31447      * validation fails (defaults to true)
31448      */
31449     /**
31450      * @cfg {Boolean} ignoreNoChange
31451      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31452      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31453      * will never be ignored.
31454      */
31455     /**
31456      * @cfg {Boolean} hideEl
31457      * False to keep the bound element visible while the editor is displayed (defaults to true)
31458      */
31459     /**
31460      * @cfg {Mixed} value
31461      * The data value of the underlying field (defaults to "")
31462      */
31463     value : "",
31464     /**
31465      * @cfg {String} alignment
31466      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31467      */
31468     alignment: "c-c?",
31469     /**
31470      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31471      * for bottom-right shadow (defaults to "frame")
31472      */
31473     shadow : "frame",
31474     /**
31475      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31476      */
31477     constrain : false,
31478     /**
31479      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31480      */
31481     completeOnEnter : false,
31482     /**
31483      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31484      */
31485     cancelOnEsc : false,
31486     /**
31487      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31488      */
31489     updateEl : false,
31490
31491     // private
31492     onRender : function(ct, position){
31493         this.el = new Roo.Layer({
31494             shadow: this.shadow,
31495             cls: "x-editor",
31496             parentEl : ct,
31497             shim : this.shim,
31498             shadowOffset:4,
31499             id: this.id,
31500             constrain: this.constrain
31501         });
31502         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31503         if(this.field.msgTarget != 'title'){
31504             this.field.msgTarget = 'qtip';
31505         }
31506         this.field.render(this.el);
31507         if(Roo.isGecko){
31508             this.field.el.dom.setAttribute('autocomplete', 'off');
31509         }
31510         this.field.on("specialkey", this.onSpecialKey, this);
31511         if(this.swallowKeys){
31512             this.field.el.swallowEvent(['keydown','keypress']);
31513         }
31514         this.field.show();
31515         this.field.on("blur", this.onBlur, this);
31516         if(this.field.grow){
31517             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31518         }
31519     },
31520
31521     onSpecialKey : function(field, e)
31522     {
31523         //Roo.log('editor onSpecialKey');
31524         if(this.completeOnEnter && e.getKey() == e.ENTER){
31525             e.stopEvent();
31526             this.completeEdit();
31527             return;
31528         }
31529         // do not fire special key otherwise it might hide close the editor...
31530         if(e.getKey() == e.ENTER){    
31531             return;
31532         }
31533         if(this.cancelOnEsc && e.getKey() == e.ESC){
31534             this.cancelEdit();
31535             return;
31536         } 
31537         this.fireEvent('specialkey', field, e);
31538     
31539     },
31540
31541     /**
31542      * Starts the editing process and shows the editor.
31543      * @param {String/HTMLElement/Element} el The element to edit
31544      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31545       * to the innerHTML of el.
31546      */
31547     startEdit : function(el, value){
31548         if(this.editing){
31549             this.completeEdit();
31550         }
31551         this.boundEl = Roo.get(el);
31552         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31553         if(!this.rendered){
31554             this.render(this.parentEl || document.body);
31555         }
31556         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31557             return;
31558         }
31559         this.startValue = v;
31560         this.field.setValue(v);
31561         if(this.autoSize){
31562             var sz = this.boundEl.getSize();
31563             switch(this.autoSize){
31564                 case "width":
31565                 this.setSize(sz.width,  "");
31566                 break;
31567                 case "height":
31568                 this.setSize("",  sz.height);
31569                 break;
31570                 default:
31571                 this.setSize(sz.width,  sz.height);
31572             }
31573         }
31574         this.el.alignTo(this.boundEl, this.alignment);
31575         this.editing = true;
31576         if(Roo.QuickTips){
31577             Roo.QuickTips.disable();
31578         }
31579         this.show();
31580     },
31581
31582     /**
31583      * Sets the height and width of this editor.
31584      * @param {Number} width The new width
31585      * @param {Number} height The new height
31586      */
31587     setSize : function(w, h){
31588         this.field.setSize(w, h);
31589         if(this.el){
31590             this.el.sync();
31591         }
31592     },
31593
31594     /**
31595      * Realigns the editor to the bound field based on the current alignment config value.
31596      */
31597     realign : function(){
31598         this.el.alignTo(this.boundEl, this.alignment);
31599     },
31600
31601     /**
31602      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31603      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31604      */
31605     completeEdit : function(remainVisible){
31606         if(!this.editing){
31607             return;
31608         }
31609         var v = this.getValue();
31610         if(this.revertInvalid !== false && !this.field.isValid()){
31611             v = this.startValue;
31612             this.cancelEdit(true);
31613         }
31614         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31615             this.editing = false;
31616             this.hide();
31617             return;
31618         }
31619         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31620             this.editing = false;
31621             if(this.updateEl && this.boundEl){
31622                 this.boundEl.update(v);
31623             }
31624             if(remainVisible !== true){
31625                 this.hide();
31626             }
31627             this.fireEvent("complete", this, v, this.startValue);
31628         }
31629     },
31630
31631     // private
31632     onShow : function(){
31633         this.el.show();
31634         if(this.hideEl !== false){
31635             this.boundEl.hide();
31636         }
31637         this.field.show();
31638         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31639             this.fixIEFocus = true;
31640             this.deferredFocus.defer(50, this);
31641         }else{
31642             this.field.focus();
31643         }
31644         this.fireEvent("startedit", this.boundEl, this.startValue);
31645     },
31646
31647     deferredFocus : function(){
31648         if(this.editing){
31649             this.field.focus();
31650         }
31651     },
31652
31653     /**
31654      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31655      * reverted to the original starting value.
31656      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31657      * cancel (defaults to false)
31658      */
31659     cancelEdit : function(remainVisible){
31660         if(this.editing){
31661             this.setValue(this.startValue);
31662             if(remainVisible !== true){
31663                 this.hide();
31664             }
31665         }
31666     },
31667
31668     // private
31669     onBlur : function(){
31670         if(this.allowBlur !== true && this.editing){
31671             this.completeEdit();
31672         }
31673     },
31674
31675     // private
31676     onHide : function(){
31677         if(this.editing){
31678             this.completeEdit();
31679             return;
31680         }
31681         this.field.blur();
31682         if(this.field.collapse){
31683             this.field.collapse();
31684         }
31685         this.el.hide();
31686         if(this.hideEl !== false){
31687             this.boundEl.show();
31688         }
31689         if(Roo.QuickTips){
31690             Roo.QuickTips.enable();
31691         }
31692     },
31693
31694     /**
31695      * Sets the data value of the editor
31696      * @param {Mixed} value Any valid value supported by the underlying field
31697      */
31698     setValue : function(v){
31699         this.field.setValue(v);
31700     },
31701
31702     /**
31703      * Gets the data value of the editor
31704      * @return {Mixed} The data value
31705      */
31706     getValue : function(){
31707         return this.field.getValue();
31708     }
31709 });/*
31710  * Based on:
31711  * Ext JS Library 1.1.1
31712  * Copyright(c) 2006-2007, Ext JS, LLC.
31713  *
31714  * Originally Released Under LGPL - original licence link has changed is not relivant.
31715  *
31716  * Fork - LGPL
31717  * <script type="text/javascript">
31718  */
31719  
31720 /**
31721  * @class Roo.BasicDialog
31722  * @extends Roo.util.Observable
31723  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31724  * <pre><code>
31725 var dlg = new Roo.BasicDialog("my-dlg", {
31726     height: 200,
31727     width: 300,
31728     minHeight: 100,
31729     minWidth: 150,
31730     modal: true,
31731     proxyDrag: true,
31732     shadow: true
31733 });
31734 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31735 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31736 dlg.addButton('Cancel', dlg.hide, dlg);
31737 dlg.show();
31738 </code></pre>
31739   <b>A Dialog should always be a direct child of the body element.</b>
31740  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31741  * @cfg {String} title Default text to display in the title bar (defaults to null)
31742  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31743  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31744  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31745  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
31746  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
31747  * (defaults to null with no animation)
31748  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
31749  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
31750  * property for valid values (defaults to 'all')
31751  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
31752  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
31753  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
31754  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
31755  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
31756  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
31757  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
31758  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
31759  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
31760  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
31761  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
31762  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
31763  * draggable = true (defaults to false)
31764  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
31765  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31766  * shadow (defaults to false)
31767  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
31768  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
31769  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
31770  * @cfg {Array} buttons Array of buttons
31771  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
31772  * @constructor
31773  * Create a new BasicDialog.
31774  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
31775  * @param {Object} config Configuration options
31776  */
31777 Roo.BasicDialog = function(el, config){
31778     this.el = Roo.get(el);
31779     var dh = Roo.DomHelper;
31780     if(!this.el && config && config.autoCreate){
31781         if(typeof config.autoCreate == "object"){
31782             if(!config.autoCreate.id){
31783                 config.autoCreate.id = el;
31784             }
31785             this.el = dh.append(document.body,
31786                         config.autoCreate, true);
31787         }else{
31788             this.el = dh.append(document.body,
31789                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
31790         }
31791     }
31792     el = this.el;
31793     el.setDisplayed(true);
31794     el.hide = this.hideAction;
31795     this.id = el.id;
31796     el.addClass("x-dlg");
31797
31798     Roo.apply(this, config);
31799
31800     this.proxy = el.createProxy("x-dlg-proxy");
31801     this.proxy.hide = this.hideAction;
31802     this.proxy.setOpacity(.5);
31803     this.proxy.hide();
31804
31805     if(config.width){
31806         el.setWidth(config.width);
31807     }
31808     if(config.height){
31809         el.setHeight(config.height);
31810     }
31811     this.size = el.getSize();
31812     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
31813         this.xy = [config.x,config.y];
31814     }else{
31815         this.xy = el.getCenterXY(true);
31816     }
31817     /** The header element @type Roo.Element */
31818     this.header = el.child("> .x-dlg-hd");
31819     /** The body element @type Roo.Element */
31820     this.body = el.child("> .x-dlg-bd");
31821     /** The footer element @type Roo.Element */
31822     this.footer = el.child("> .x-dlg-ft");
31823
31824     if(!this.header){
31825         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
31826     }
31827     if(!this.body){
31828         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
31829     }
31830
31831     this.header.unselectable();
31832     if(this.title){
31833         this.header.update(this.title);
31834     }
31835     // this element allows the dialog to be focused for keyboard event
31836     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
31837     this.focusEl.swallowEvent("click", true);
31838
31839     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
31840
31841     // wrap the body and footer for special rendering
31842     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
31843     if(this.footer){
31844         this.bwrap.dom.appendChild(this.footer.dom);
31845     }
31846
31847     this.bg = this.el.createChild({
31848         tag: "div", cls:"x-dlg-bg",
31849         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
31850     });
31851     this.centerBg = this.bg.child("div.x-dlg-bg-center");
31852
31853
31854     if(this.autoScroll !== false && !this.autoTabs){
31855         this.body.setStyle("overflow", "auto");
31856     }
31857
31858     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
31859
31860     if(this.closable !== false){
31861         this.el.addClass("x-dlg-closable");
31862         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
31863         this.close.on("click", this.closeClick, this);
31864         this.close.addClassOnOver("x-dlg-close-over");
31865     }
31866     if(this.collapsible !== false){
31867         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
31868         this.collapseBtn.on("click", this.collapseClick, this);
31869         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
31870         this.header.on("dblclick", this.collapseClick, this);
31871     }
31872     if(this.resizable !== false){
31873         this.el.addClass("x-dlg-resizable");
31874         this.resizer = new Roo.Resizable(el, {
31875             minWidth: this.minWidth || 80,
31876             minHeight:this.minHeight || 80,
31877             handles: this.resizeHandles || "all",
31878             pinned: true
31879         });
31880         this.resizer.on("beforeresize", this.beforeResize, this);
31881         this.resizer.on("resize", this.onResize, this);
31882     }
31883     if(this.draggable !== false){
31884         el.addClass("x-dlg-draggable");
31885         if (!this.proxyDrag) {
31886             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
31887         }
31888         else {
31889             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
31890         }
31891         dd.setHandleElId(this.header.id);
31892         dd.endDrag = this.endMove.createDelegate(this);
31893         dd.startDrag = this.startMove.createDelegate(this);
31894         dd.onDrag = this.onDrag.createDelegate(this);
31895         dd.scroll = false;
31896         this.dd = dd;
31897     }
31898     if(this.modal){
31899         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
31900         this.mask.enableDisplayMode("block");
31901         this.mask.hide();
31902         this.el.addClass("x-dlg-modal");
31903     }
31904     if(this.shadow){
31905         this.shadow = new Roo.Shadow({
31906             mode : typeof this.shadow == "string" ? this.shadow : "sides",
31907             offset : this.shadowOffset
31908         });
31909     }else{
31910         this.shadowOffset = 0;
31911     }
31912     if(Roo.useShims && this.shim !== false){
31913         this.shim = this.el.createShim();
31914         this.shim.hide = this.hideAction;
31915         this.shim.hide();
31916     }else{
31917         this.shim = false;
31918     }
31919     if(this.autoTabs){
31920         this.initTabs();
31921     }
31922     if (this.buttons) { 
31923         var bts= this.buttons;
31924         this.buttons = [];
31925         Roo.each(bts, function(b) {
31926             this.addButton(b);
31927         }, this);
31928     }
31929     
31930     
31931     this.addEvents({
31932         /**
31933          * @event keydown
31934          * Fires when a key is pressed
31935          * @param {Roo.BasicDialog} this
31936          * @param {Roo.EventObject} e
31937          */
31938         "keydown" : true,
31939         /**
31940          * @event move
31941          * Fires when this dialog is moved by the user.
31942          * @param {Roo.BasicDialog} this
31943          * @param {Number} x The new page X
31944          * @param {Number} y The new page Y
31945          */
31946         "move" : true,
31947         /**
31948          * @event resize
31949          * Fires when this dialog is resized by the user.
31950          * @param {Roo.BasicDialog} this
31951          * @param {Number} width The new width
31952          * @param {Number} height The new height
31953          */
31954         "resize" : true,
31955         /**
31956          * @event beforehide
31957          * Fires before this dialog is hidden.
31958          * @param {Roo.BasicDialog} this
31959          */
31960         "beforehide" : true,
31961         /**
31962          * @event hide
31963          * Fires when this dialog is hidden.
31964          * @param {Roo.BasicDialog} this
31965          */
31966         "hide" : true,
31967         /**
31968          * @event beforeshow
31969          * Fires before this dialog is shown.
31970          * @param {Roo.BasicDialog} this
31971          */
31972         "beforeshow" : true,
31973         /**
31974          * @event show
31975          * Fires when this dialog is shown.
31976          * @param {Roo.BasicDialog} this
31977          */
31978         "show" : true
31979     });
31980     el.on("keydown", this.onKeyDown, this);
31981     el.on("mousedown", this.toFront, this);
31982     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
31983     this.el.hide();
31984     Roo.DialogManager.register(this);
31985     Roo.BasicDialog.superclass.constructor.call(this);
31986 };
31987
31988 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
31989     shadowOffset: Roo.isIE ? 6 : 5,
31990     minHeight: 80,
31991     minWidth: 200,
31992     minButtonWidth: 75,
31993     defaultButton: null,
31994     buttonAlign: "right",
31995     tabTag: 'div',
31996     firstShow: true,
31997
31998     /**
31999      * Sets the dialog title text
32000      * @param {String} text The title text to display
32001      * @return {Roo.BasicDialog} this
32002      */
32003     setTitle : function(text){
32004         this.header.update(text);
32005         return this;
32006     },
32007
32008     // private
32009     closeClick : function(){
32010         this.hide();
32011     },
32012
32013     // private
32014     collapseClick : function(){
32015         this[this.collapsed ? "expand" : "collapse"]();
32016     },
32017
32018     /**
32019      * Collapses the dialog to its minimized state (only the title bar is visible).
32020      * Equivalent to the user clicking the collapse dialog button.
32021      */
32022     collapse : function(){
32023         if(!this.collapsed){
32024             this.collapsed = true;
32025             this.el.addClass("x-dlg-collapsed");
32026             this.restoreHeight = this.el.getHeight();
32027             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32028         }
32029     },
32030
32031     /**
32032      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32033      * clicking the expand dialog button.
32034      */
32035     expand : function(){
32036         if(this.collapsed){
32037             this.collapsed = false;
32038             this.el.removeClass("x-dlg-collapsed");
32039             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32040         }
32041     },
32042
32043     /**
32044      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32045      * @return {Roo.TabPanel} The tabs component
32046      */
32047     initTabs : function(){
32048         var tabs = this.getTabs();
32049         while(tabs.getTab(0)){
32050             tabs.removeTab(0);
32051         }
32052         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32053             var dom = el.dom;
32054             tabs.addTab(Roo.id(dom), dom.title);
32055             dom.title = "";
32056         });
32057         tabs.activate(0);
32058         return tabs;
32059     },
32060
32061     // private
32062     beforeResize : function(){
32063         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32064     },
32065
32066     // private
32067     onResize : function(){
32068         this.refreshSize();
32069         this.syncBodyHeight();
32070         this.adjustAssets();
32071         this.focus();
32072         this.fireEvent("resize", this, this.size.width, this.size.height);
32073     },
32074
32075     // private
32076     onKeyDown : function(e){
32077         if(this.isVisible()){
32078             this.fireEvent("keydown", this, e);
32079         }
32080     },
32081
32082     /**
32083      * Resizes the dialog.
32084      * @param {Number} width
32085      * @param {Number} height
32086      * @return {Roo.BasicDialog} this
32087      */
32088     resizeTo : function(width, height){
32089         this.el.setSize(width, height);
32090         this.size = {width: width, height: height};
32091         this.syncBodyHeight();
32092         if(this.fixedcenter){
32093             this.center();
32094         }
32095         if(this.isVisible()){
32096             this.constrainXY();
32097             this.adjustAssets();
32098         }
32099         this.fireEvent("resize", this, width, height);
32100         return this;
32101     },
32102
32103
32104     /**
32105      * Resizes the dialog to fit the specified content size.
32106      * @param {Number} width
32107      * @param {Number} height
32108      * @return {Roo.BasicDialog} this
32109      */
32110     setContentSize : function(w, h){
32111         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32112         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32113         //if(!this.el.isBorderBox()){
32114             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32115             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32116         //}
32117         if(this.tabs){
32118             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32119             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32120         }
32121         this.resizeTo(w, h);
32122         return this;
32123     },
32124
32125     /**
32126      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32127      * executed in response to a particular key being pressed while the dialog is active.
32128      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32129      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32130      * @param {Function} fn The function to call
32131      * @param {Object} scope (optional) The scope of the function
32132      * @return {Roo.BasicDialog} this
32133      */
32134     addKeyListener : function(key, fn, scope){
32135         var keyCode, shift, ctrl, alt;
32136         if(typeof key == "object" && !(key instanceof Array)){
32137             keyCode = key["key"];
32138             shift = key["shift"];
32139             ctrl = key["ctrl"];
32140             alt = key["alt"];
32141         }else{
32142             keyCode = key;
32143         }
32144         var handler = function(dlg, e){
32145             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32146                 var k = e.getKey();
32147                 if(keyCode instanceof Array){
32148                     for(var i = 0, len = keyCode.length; i < len; i++){
32149                         if(keyCode[i] == k){
32150                           fn.call(scope || window, dlg, k, e);
32151                           return;
32152                         }
32153                     }
32154                 }else{
32155                     if(k == keyCode){
32156                         fn.call(scope || window, dlg, k, e);
32157                     }
32158                 }
32159             }
32160         };
32161         this.on("keydown", handler);
32162         return this;
32163     },
32164
32165     /**
32166      * Returns the TabPanel component (creates it if it doesn't exist).
32167      * Note: If you wish to simply check for the existence of tabs without creating them,
32168      * check for a null 'tabs' property.
32169      * @return {Roo.TabPanel} The tabs component
32170      */
32171     getTabs : function(){
32172         if(!this.tabs){
32173             this.el.addClass("x-dlg-auto-tabs");
32174             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32175             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32176         }
32177         return this.tabs;
32178     },
32179
32180     /**
32181      * Adds a button to the footer section of the dialog.
32182      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32183      * object or a valid Roo.DomHelper element config
32184      * @param {Function} handler The function called when the button is clicked
32185      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32186      * @return {Roo.Button} The new button
32187      */
32188     addButton : function(config, handler, scope){
32189         var dh = Roo.DomHelper;
32190         if(!this.footer){
32191             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32192         }
32193         if(!this.btnContainer){
32194             var tb = this.footer.createChild({
32195
32196                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32197                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32198             }, null, true);
32199             this.btnContainer = tb.firstChild.firstChild.firstChild;
32200         }
32201         var bconfig = {
32202             handler: handler,
32203             scope: scope,
32204             minWidth: this.minButtonWidth,
32205             hideParent:true
32206         };
32207         if(typeof config == "string"){
32208             bconfig.text = config;
32209         }else{
32210             if(config.tag){
32211                 bconfig.dhconfig = config;
32212             }else{
32213                 Roo.apply(bconfig, config);
32214             }
32215         }
32216         var fc = false;
32217         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32218             bconfig.position = Math.max(0, bconfig.position);
32219             fc = this.btnContainer.childNodes[bconfig.position];
32220         }
32221          
32222         var btn = new Roo.Button(
32223             fc ? 
32224                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32225                 : this.btnContainer.appendChild(document.createElement("td")),
32226             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32227             bconfig
32228         );
32229         this.syncBodyHeight();
32230         if(!this.buttons){
32231             /**
32232              * Array of all the buttons that have been added to this dialog via addButton
32233              * @type Array
32234              */
32235             this.buttons = [];
32236         }
32237         this.buttons.push(btn);
32238         return btn;
32239     },
32240
32241     /**
32242      * Sets the default button to be focused when the dialog is displayed.
32243      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32244      * @return {Roo.BasicDialog} this
32245      */
32246     setDefaultButton : function(btn){
32247         this.defaultButton = btn;
32248         return this;
32249     },
32250
32251     // private
32252     getHeaderFooterHeight : function(safe){
32253         var height = 0;
32254         if(this.header){
32255            height += this.header.getHeight();
32256         }
32257         if(this.footer){
32258            var fm = this.footer.getMargins();
32259             height += (this.footer.getHeight()+fm.top+fm.bottom);
32260         }
32261         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32262         height += this.centerBg.getPadding("tb");
32263         return height;
32264     },
32265
32266     // private
32267     syncBodyHeight : function()
32268     {
32269         var bd = this.body, // the text
32270             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32271             bw = this.bwrap;
32272         var height = this.size.height - this.getHeaderFooterHeight(false);
32273         bd.setHeight(height-bd.getMargins("tb"));
32274         var hh = this.header.getHeight();
32275         var h = this.size.height-hh;
32276         cb.setHeight(h);
32277         
32278         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32279         bw.setHeight(h-cb.getPadding("tb"));
32280         
32281         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32282         bd.setWidth(bw.getWidth(true));
32283         if(this.tabs){
32284             this.tabs.syncHeight();
32285             if(Roo.isIE){
32286                 this.tabs.el.repaint();
32287             }
32288         }
32289     },
32290
32291     /**
32292      * Restores the previous state of the dialog if Roo.state is configured.
32293      * @return {Roo.BasicDialog} this
32294      */
32295     restoreState : function(){
32296         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32297         if(box && box.width){
32298             this.xy = [box.x, box.y];
32299             this.resizeTo(box.width, box.height);
32300         }
32301         return this;
32302     },
32303
32304     // private
32305     beforeShow : function(){
32306         this.expand();
32307         if(this.fixedcenter){
32308             this.xy = this.el.getCenterXY(true);
32309         }
32310         if(this.modal){
32311             Roo.get(document.body).addClass("x-body-masked");
32312             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32313             this.mask.show();
32314         }
32315         this.constrainXY();
32316     },
32317
32318     // private
32319     animShow : function(){
32320         var b = Roo.get(this.animateTarget).getBox();
32321         this.proxy.setSize(b.width, b.height);
32322         this.proxy.setLocation(b.x, b.y);
32323         this.proxy.show();
32324         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32325                     true, .35, this.showEl.createDelegate(this));
32326     },
32327
32328     /**
32329      * Shows the dialog.
32330      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32331      * @return {Roo.BasicDialog} this
32332      */
32333     show : function(animateTarget){
32334         if (this.fireEvent("beforeshow", this) === false){
32335             return;
32336         }
32337         if(this.syncHeightBeforeShow){
32338             this.syncBodyHeight();
32339         }else if(this.firstShow){
32340             this.firstShow = false;
32341             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32342         }
32343         this.animateTarget = animateTarget || this.animateTarget;
32344         if(!this.el.isVisible()){
32345             this.beforeShow();
32346             if(this.animateTarget && Roo.get(this.animateTarget)){
32347                 this.animShow();
32348             }else{
32349                 this.showEl();
32350             }
32351         }
32352         return this;
32353     },
32354
32355     // private
32356     showEl : function(){
32357         this.proxy.hide();
32358         this.el.setXY(this.xy);
32359         this.el.show();
32360         this.adjustAssets(true);
32361         this.toFront();
32362         this.focus();
32363         // IE peekaboo bug - fix found by Dave Fenwick
32364         if(Roo.isIE){
32365             this.el.repaint();
32366         }
32367         this.fireEvent("show", this);
32368     },
32369
32370     /**
32371      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32372      * dialog itself will receive focus.
32373      */
32374     focus : function(){
32375         if(this.defaultButton){
32376             this.defaultButton.focus();
32377         }else{
32378             this.focusEl.focus();
32379         }
32380     },
32381
32382     // private
32383     constrainXY : function(){
32384         if(this.constraintoviewport !== false){
32385             if(!this.viewSize){
32386                 if(this.container){
32387                     var s = this.container.getSize();
32388                     this.viewSize = [s.width, s.height];
32389                 }else{
32390                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32391                 }
32392             }
32393             var s = Roo.get(this.container||document).getScroll();
32394
32395             var x = this.xy[0], y = this.xy[1];
32396             var w = this.size.width, h = this.size.height;
32397             var vw = this.viewSize[0], vh = this.viewSize[1];
32398             // only move it if it needs it
32399             var moved = false;
32400             // first validate right/bottom
32401             if(x + w > vw+s.left){
32402                 x = vw - w;
32403                 moved = true;
32404             }
32405             if(y + h > vh+s.top){
32406                 y = vh - h;
32407                 moved = true;
32408             }
32409             // then make sure top/left isn't negative
32410             if(x < s.left){
32411                 x = s.left;
32412                 moved = true;
32413             }
32414             if(y < s.top){
32415                 y = s.top;
32416                 moved = true;
32417             }
32418             if(moved){
32419                 // cache xy
32420                 this.xy = [x, y];
32421                 if(this.isVisible()){
32422                     this.el.setLocation(x, y);
32423                     this.adjustAssets();
32424                 }
32425             }
32426         }
32427     },
32428
32429     // private
32430     onDrag : function(){
32431         if(!this.proxyDrag){
32432             this.xy = this.el.getXY();
32433             this.adjustAssets();
32434         }
32435     },
32436
32437     // private
32438     adjustAssets : function(doShow){
32439         var x = this.xy[0], y = this.xy[1];
32440         var w = this.size.width, h = this.size.height;
32441         if(doShow === true){
32442             if(this.shadow){
32443                 this.shadow.show(this.el);
32444             }
32445             if(this.shim){
32446                 this.shim.show();
32447             }
32448         }
32449         if(this.shadow && this.shadow.isVisible()){
32450             this.shadow.show(this.el);
32451         }
32452         if(this.shim && this.shim.isVisible()){
32453             this.shim.setBounds(x, y, w, h);
32454         }
32455     },
32456
32457     // private
32458     adjustViewport : function(w, h){
32459         if(!w || !h){
32460             w = Roo.lib.Dom.getViewWidth();
32461             h = Roo.lib.Dom.getViewHeight();
32462         }
32463         // cache the size
32464         this.viewSize = [w, h];
32465         if(this.modal && this.mask.isVisible()){
32466             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32467             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32468         }
32469         if(this.isVisible()){
32470             this.constrainXY();
32471         }
32472     },
32473
32474     /**
32475      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32476      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32477      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32478      */
32479     destroy : function(removeEl){
32480         if(this.isVisible()){
32481             this.animateTarget = null;
32482             this.hide();
32483         }
32484         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32485         if(this.tabs){
32486             this.tabs.destroy(removeEl);
32487         }
32488         Roo.destroy(
32489              this.shim,
32490              this.proxy,
32491              this.resizer,
32492              this.close,
32493              this.mask
32494         );
32495         if(this.dd){
32496             this.dd.unreg();
32497         }
32498         if(this.buttons){
32499            for(var i = 0, len = this.buttons.length; i < len; i++){
32500                this.buttons[i].destroy();
32501            }
32502         }
32503         this.el.removeAllListeners();
32504         if(removeEl === true){
32505             this.el.update("");
32506             this.el.remove();
32507         }
32508         Roo.DialogManager.unregister(this);
32509     },
32510
32511     // private
32512     startMove : function(){
32513         if(this.proxyDrag){
32514             this.proxy.show();
32515         }
32516         if(this.constraintoviewport !== false){
32517             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32518         }
32519     },
32520
32521     // private
32522     endMove : function(){
32523         if(!this.proxyDrag){
32524             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32525         }else{
32526             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32527             this.proxy.hide();
32528         }
32529         this.refreshSize();
32530         this.adjustAssets();
32531         this.focus();
32532         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32533     },
32534
32535     /**
32536      * Brings this dialog to the front of any other visible dialogs
32537      * @return {Roo.BasicDialog} this
32538      */
32539     toFront : function(){
32540         Roo.DialogManager.bringToFront(this);
32541         return this;
32542     },
32543
32544     /**
32545      * Sends this dialog to the back (under) of any other visible dialogs
32546      * @return {Roo.BasicDialog} this
32547      */
32548     toBack : function(){
32549         Roo.DialogManager.sendToBack(this);
32550         return this;
32551     },
32552
32553     /**
32554      * Centers this dialog in the viewport
32555      * @return {Roo.BasicDialog} this
32556      */
32557     center : function(){
32558         var xy = this.el.getCenterXY(true);
32559         this.moveTo(xy[0], xy[1]);
32560         return this;
32561     },
32562
32563     /**
32564      * Moves the dialog's top-left corner to the specified point
32565      * @param {Number} x
32566      * @param {Number} y
32567      * @return {Roo.BasicDialog} this
32568      */
32569     moveTo : function(x, y){
32570         this.xy = [x,y];
32571         if(this.isVisible()){
32572             this.el.setXY(this.xy);
32573             this.adjustAssets();
32574         }
32575         return this;
32576     },
32577
32578     /**
32579      * Aligns the dialog to the specified element
32580      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32581      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32582      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32583      * @return {Roo.BasicDialog} this
32584      */
32585     alignTo : function(element, position, offsets){
32586         this.xy = this.el.getAlignToXY(element, position, offsets);
32587         if(this.isVisible()){
32588             this.el.setXY(this.xy);
32589             this.adjustAssets();
32590         }
32591         return this;
32592     },
32593
32594     /**
32595      * Anchors an element to another element and realigns it when the window is resized.
32596      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32597      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32598      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32599      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32600      * is a number, it is used as the buffer delay (defaults to 50ms).
32601      * @return {Roo.BasicDialog} this
32602      */
32603     anchorTo : function(el, alignment, offsets, monitorScroll){
32604         var action = function(){
32605             this.alignTo(el, alignment, offsets);
32606         };
32607         Roo.EventManager.onWindowResize(action, this);
32608         var tm = typeof monitorScroll;
32609         if(tm != 'undefined'){
32610             Roo.EventManager.on(window, 'scroll', action, this,
32611                 {buffer: tm == 'number' ? monitorScroll : 50});
32612         }
32613         action.call(this);
32614         return this;
32615     },
32616
32617     /**
32618      * Returns true if the dialog is visible
32619      * @return {Boolean}
32620      */
32621     isVisible : function(){
32622         return this.el.isVisible();
32623     },
32624
32625     // private
32626     animHide : function(callback){
32627         var b = Roo.get(this.animateTarget).getBox();
32628         this.proxy.show();
32629         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32630         this.el.hide();
32631         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32632                     this.hideEl.createDelegate(this, [callback]));
32633     },
32634
32635     /**
32636      * Hides the dialog.
32637      * @param {Function} callback (optional) Function to call when the dialog is hidden
32638      * @return {Roo.BasicDialog} this
32639      */
32640     hide : function(callback){
32641         if (this.fireEvent("beforehide", this) === false){
32642             return;
32643         }
32644         if(this.shadow){
32645             this.shadow.hide();
32646         }
32647         if(this.shim) {
32648           this.shim.hide();
32649         }
32650         // sometimes animateTarget seems to get set.. causing problems...
32651         // this just double checks..
32652         if(this.animateTarget && Roo.get(this.animateTarget)) {
32653            this.animHide(callback);
32654         }else{
32655             this.el.hide();
32656             this.hideEl(callback);
32657         }
32658         return this;
32659     },
32660
32661     // private
32662     hideEl : function(callback){
32663         this.proxy.hide();
32664         if(this.modal){
32665             this.mask.hide();
32666             Roo.get(document.body).removeClass("x-body-masked");
32667         }
32668         this.fireEvent("hide", this);
32669         if(typeof callback == "function"){
32670             callback();
32671         }
32672     },
32673
32674     // private
32675     hideAction : function(){
32676         this.setLeft("-10000px");
32677         this.setTop("-10000px");
32678         this.setStyle("visibility", "hidden");
32679     },
32680
32681     // private
32682     refreshSize : function(){
32683         this.size = this.el.getSize();
32684         this.xy = this.el.getXY();
32685         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32686     },
32687
32688     // private
32689     // z-index is managed by the DialogManager and may be overwritten at any time
32690     setZIndex : function(index){
32691         if(this.modal){
32692             this.mask.setStyle("z-index", index);
32693         }
32694         if(this.shim){
32695             this.shim.setStyle("z-index", ++index);
32696         }
32697         if(this.shadow){
32698             this.shadow.setZIndex(++index);
32699         }
32700         this.el.setStyle("z-index", ++index);
32701         if(this.proxy){
32702             this.proxy.setStyle("z-index", ++index);
32703         }
32704         if(this.resizer){
32705             this.resizer.proxy.setStyle("z-index", ++index);
32706         }
32707
32708         this.lastZIndex = index;
32709     },
32710
32711     /**
32712      * Returns the element for this dialog
32713      * @return {Roo.Element} The underlying dialog Element
32714      */
32715     getEl : function(){
32716         return this.el;
32717     }
32718 });
32719
32720 /**
32721  * @class Roo.DialogManager
32722  * Provides global access to BasicDialogs that have been created and
32723  * support for z-indexing (layering) multiple open dialogs.
32724  */
32725 Roo.DialogManager = function(){
32726     var list = {};
32727     var accessList = [];
32728     var front = null;
32729
32730     // private
32731     var sortDialogs = function(d1, d2){
32732         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32733     };
32734
32735     // private
32736     var orderDialogs = function(){
32737         accessList.sort(sortDialogs);
32738         var seed = Roo.DialogManager.zseed;
32739         for(var i = 0, len = accessList.length; i < len; i++){
32740             var dlg = accessList[i];
32741             if(dlg){
32742                 dlg.setZIndex(seed + (i*10));
32743             }
32744         }
32745     };
32746
32747     return {
32748         /**
32749          * The starting z-index for BasicDialogs (defaults to 9000)
32750          * @type Number The z-index value
32751          */
32752         zseed : 9000,
32753
32754         // private
32755         register : function(dlg){
32756             list[dlg.id] = dlg;
32757             accessList.push(dlg);
32758         },
32759
32760         // private
32761         unregister : function(dlg){
32762             delete list[dlg.id];
32763             var i=0;
32764             var len=0;
32765             if(!accessList.indexOf){
32766                 for(  i = 0, len = accessList.length; i < len; i++){
32767                     if(accessList[i] == dlg){
32768                         accessList.splice(i, 1);
32769                         return;
32770                     }
32771                 }
32772             }else{
32773                  i = accessList.indexOf(dlg);
32774                 if(i != -1){
32775                     accessList.splice(i, 1);
32776                 }
32777             }
32778         },
32779
32780         /**
32781          * Gets a registered dialog by id
32782          * @param {String/Object} id The id of the dialog or a dialog
32783          * @return {Roo.BasicDialog} this
32784          */
32785         get : function(id){
32786             return typeof id == "object" ? id : list[id];
32787         },
32788
32789         /**
32790          * Brings the specified dialog to the front
32791          * @param {String/Object} dlg The id of the dialog or a dialog
32792          * @return {Roo.BasicDialog} this
32793          */
32794         bringToFront : function(dlg){
32795             dlg = this.get(dlg);
32796             if(dlg != front){
32797                 front = dlg;
32798                 dlg._lastAccess = new Date().getTime();
32799                 orderDialogs();
32800             }
32801             return dlg;
32802         },
32803
32804         /**
32805          * Sends the specified dialog to the back
32806          * @param {String/Object} dlg The id of the dialog or a dialog
32807          * @return {Roo.BasicDialog} this
32808          */
32809         sendToBack : function(dlg){
32810             dlg = this.get(dlg);
32811             dlg._lastAccess = -(new Date().getTime());
32812             orderDialogs();
32813             return dlg;
32814         },
32815
32816         /**
32817          * Hides all dialogs
32818          */
32819         hideAll : function(){
32820             for(var id in list){
32821                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
32822                     list[id].hide();
32823                 }
32824             }
32825         }
32826     };
32827 }();
32828
32829 /**
32830  * @class Roo.LayoutDialog
32831  * @extends Roo.BasicDialog
32832  * Dialog which provides adjustments for working with a layout in a Dialog.
32833  * Add your necessary layout config options to the dialog's config.<br>
32834  * Example usage (including a nested layout):
32835  * <pre><code>
32836 if(!dialog){
32837     dialog = new Roo.LayoutDialog("download-dlg", {
32838         modal: true,
32839         width:600,
32840         height:450,
32841         shadow:true,
32842         minWidth:500,
32843         minHeight:350,
32844         autoTabs:true,
32845         proxyDrag:true,
32846         // layout config merges with the dialog config
32847         center:{
32848             tabPosition: "top",
32849             alwaysShowTabs: true
32850         }
32851     });
32852     dialog.addKeyListener(27, dialog.hide, dialog);
32853     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
32854     dialog.addButton("Build It!", this.getDownload, this);
32855
32856     // we can even add nested layouts
32857     var innerLayout = new Roo.BorderLayout("dl-inner", {
32858         east: {
32859             initialSize: 200,
32860             autoScroll:true,
32861             split:true
32862         },
32863         center: {
32864             autoScroll:true
32865         }
32866     });
32867     innerLayout.beginUpdate();
32868     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
32869     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
32870     innerLayout.endUpdate(true);
32871
32872     var layout = dialog.getLayout();
32873     layout.beginUpdate();
32874     layout.add("center", new Roo.ContentPanel("standard-panel",
32875                         {title: "Download the Source", fitToFrame:true}));
32876     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
32877                {title: "Build your own roo.js"}));
32878     layout.getRegion("center").showPanel(sp);
32879     layout.endUpdate();
32880 }
32881 </code></pre>
32882     * @constructor
32883     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
32884     * @param {Object} config configuration options
32885   */
32886 Roo.LayoutDialog = function(el, cfg){
32887     
32888     var config=  cfg;
32889     if (typeof(cfg) == 'undefined') {
32890         config = Roo.apply({}, el);
32891         // not sure why we use documentElement here.. - it should always be body.
32892         // IE7 borks horribly if we use documentElement.
32893         // webkit also does not like documentElement - it creates a body element...
32894         el = Roo.get( document.body || document.documentElement ).createChild();
32895         //config.autoCreate = true;
32896     }
32897     
32898     
32899     config.autoTabs = false;
32900     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
32901     this.body.setStyle({overflow:"hidden", position:"relative"});
32902     this.layout = new Roo.BorderLayout(this.body.dom, config);
32903     this.layout.monitorWindowResize = false;
32904     this.el.addClass("x-dlg-auto-layout");
32905     // fix case when center region overwrites center function
32906     this.center = Roo.BasicDialog.prototype.center;
32907     this.on("show", this.layout.layout, this.layout, true);
32908     if (config.items) {
32909         var xitems = config.items;
32910         delete config.items;
32911         Roo.each(xitems, this.addxtype, this);
32912     }
32913     
32914     
32915 };
32916 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
32917     /**
32918      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
32919      * @deprecated
32920      */
32921     endUpdate : function(){
32922         this.layout.endUpdate();
32923     },
32924
32925     /**
32926      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
32927      *  @deprecated
32928      */
32929     beginUpdate : function(){
32930         this.layout.beginUpdate();
32931     },
32932
32933     /**
32934      * Get the BorderLayout for this dialog
32935      * @return {Roo.BorderLayout}
32936      */
32937     getLayout : function(){
32938         return this.layout;
32939     },
32940
32941     showEl : function(){
32942         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
32943         if(Roo.isIE7){
32944             this.layout.layout();
32945         }
32946     },
32947
32948     // private
32949     // Use the syncHeightBeforeShow config option to control this automatically
32950     syncBodyHeight : function(){
32951         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
32952         if(this.layout){this.layout.layout();}
32953     },
32954     
32955       /**
32956      * Add an xtype element (actually adds to the layout.)
32957      * @return {Object} xdata xtype object data.
32958      */
32959     
32960     addxtype : function(c) {
32961         return this.layout.addxtype(c);
32962     }
32963 });/*
32964  * Based on:
32965  * Ext JS Library 1.1.1
32966  * Copyright(c) 2006-2007, Ext JS, LLC.
32967  *
32968  * Originally Released Under LGPL - original licence link has changed is not relivant.
32969  *
32970  * Fork - LGPL
32971  * <script type="text/javascript">
32972  */
32973  
32974 /**
32975  * @class Roo.MessageBox
32976  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
32977  * Example usage:
32978  *<pre><code>
32979 // Basic alert:
32980 Roo.Msg.alert('Status', 'Changes saved successfully.');
32981
32982 // Prompt for user data:
32983 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
32984     if (btn == 'ok'){
32985         // process text value...
32986     }
32987 });
32988
32989 // Show a dialog using config options:
32990 Roo.Msg.show({
32991    title:'Save Changes?',
32992    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
32993    buttons: Roo.Msg.YESNOCANCEL,
32994    fn: processResult,
32995    animEl: 'elId'
32996 });
32997 </code></pre>
32998  * @singleton
32999  */
33000 Roo.MessageBox = function(){
33001     var dlg, opt, mask, waitTimer;
33002     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33003     var buttons, activeTextEl, bwidth;
33004
33005     // private
33006     var handleButton = function(button){
33007         dlg.hide();
33008         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33009     };
33010
33011     // private
33012     var handleHide = function(){
33013         if(opt && opt.cls){
33014             dlg.el.removeClass(opt.cls);
33015         }
33016         if(waitTimer){
33017             Roo.TaskMgr.stop(waitTimer);
33018             waitTimer = null;
33019         }
33020     };
33021
33022     // private
33023     var updateButtons = function(b){
33024         var width = 0;
33025         if(!b){
33026             buttons["ok"].hide();
33027             buttons["cancel"].hide();
33028             buttons["yes"].hide();
33029             buttons["no"].hide();
33030             dlg.footer.dom.style.display = 'none';
33031             return width;
33032         }
33033         dlg.footer.dom.style.display = '';
33034         for(var k in buttons){
33035             if(typeof buttons[k] != "function"){
33036                 if(b[k]){
33037                     buttons[k].show();
33038                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33039                     width += buttons[k].el.getWidth()+15;
33040                 }else{
33041                     buttons[k].hide();
33042                 }
33043             }
33044         }
33045         return width;
33046     };
33047
33048     // private
33049     var handleEsc = function(d, k, e){
33050         if(opt && opt.closable !== false){
33051             dlg.hide();
33052         }
33053         if(e){
33054             e.stopEvent();
33055         }
33056     };
33057
33058     return {
33059         /**
33060          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33061          * @return {Roo.BasicDialog} The BasicDialog element
33062          */
33063         getDialog : function(){
33064            if(!dlg){
33065                 dlg = new Roo.BasicDialog("x-msg-box", {
33066                     autoCreate : true,
33067                     shadow: true,
33068                     draggable: true,
33069                     resizable:false,
33070                     constraintoviewport:false,
33071                     fixedcenter:true,
33072                     collapsible : false,
33073                     shim:true,
33074                     modal: true,
33075                     width:400, height:100,
33076                     buttonAlign:"center",
33077                     closeClick : function(){
33078                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33079                             handleButton("no");
33080                         }else{
33081                             handleButton("cancel");
33082                         }
33083                     }
33084                 });
33085                 dlg.on("hide", handleHide);
33086                 mask = dlg.mask;
33087                 dlg.addKeyListener(27, handleEsc);
33088                 buttons = {};
33089                 var bt = this.buttonText;
33090                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33091                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33092                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33093                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33094                 bodyEl = dlg.body.createChild({
33095
33096                     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>'
33097                 });
33098                 msgEl = bodyEl.dom.firstChild;
33099                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33100                 textboxEl.enableDisplayMode();
33101                 textboxEl.addKeyListener([10,13], function(){
33102                     if(dlg.isVisible() && opt && opt.buttons){
33103                         if(opt.buttons.ok){
33104                             handleButton("ok");
33105                         }else if(opt.buttons.yes){
33106                             handleButton("yes");
33107                         }
33108                     }
33109                 });
33110                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33111                 textareaEl.enableDisplayMode();
33112                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33113                 progressEl.enableDisplayMode();
33114                 var pf = progressEl.dom.firstChild;
33115                 if (pf) {
33116                     pp = Roo.get(pf.firstChild);
33117                     pp.setHeight(pf.offsetHeight);
33118                 }
33119                 
33120             }
33121             return dlg;
33122         },
33123
33124         /**
33125          * Updates the message box body text
33126          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33127          * the XHTML-compliant non-breaking space character '&amp;#160;')
33128          * @return {Roo.MessageBox} This message box
33129          */
33130         updateText : function(text){
33131             if(!dlg.isVisible() && !opt.width){
33132                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33133             }
33134             msgEl.innerHTML = text || '&#160;';
33135       
33136             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33137             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33138             var w = Math.max(
33139                     Math.min(opt.width || cw , this.maxWidth), 
33140                     Math.max(opt.minWidth || this.minWidth, bwidth)
33141             );
33142             if(opt.prompt){
33143                 activeTextEl.setWidth(w);
33144             }
33145             if(dlg.isVisible()){
33146                 dlg.fixedcenter = false;
33147             }
33148             // to big, make it scroll. = But as usual stupid IE does not support
33149             // !important..
33150             
33151             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33152                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33153                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33154             } else {
33155                 bodyEl.dom.style.height = '';
33156                 bodyEl.dom.style.overflowY = '';
33157             }
33158             if (cw > w) {
33159                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33160             } else {
33161                 bodyEl.dom.style.overflowX = '';
33162             }
33163             
33164             dlg.setContentSize(w, bodyEl.getHeight());
33165             if(dlg.isVisible()){
33166                 dlg.fixedcenter = true;
33167             }
33168             return this;
33169         },
33170
33171         /**
33172          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33173          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33174          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33175          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33176          * @return {Roo.MessageBox} This message box
33177          */
33178         updateProgress : function(value, text){
33179             if(text){
33180                 this.updateText(text);
33181             }
33182             if (pp) { // weird bug on my firefox - for some reason this is not defined
33183                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33184             }
33185             return this;
33186         },        
33187
33188         /**
33189          * Returns true if the message box is currently displayed
33190          * @return {Boolean} True if the message box is visible, else false
33191          */
33192         isVisible : function(){
33193             return dlg && dlg.isVisible();  
33194         },
33195
33196         /**
33197          * Hides the message box if it is displayed
33198          */
33199         hide : function(){
33200             if(this.isVisible()){
33201                 dlg.hide();
33202             }  
33203         },
33204
33205         /**
33206          * Displays a new message box, or reinitializes an existing message box, based on the config options
33207          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33208          * The following config object properties are supported:
33209          * <pre>
33210 Property    Type             Description
33211 ----------  ---------------  ------------------------------------------------------------------------------------
33212 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33213                                    closes (defaults to undefined)
33214 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33215                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33216 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33217                                    progress and wait dialogs will ignore this property and always hide the
33218                                    close button as they can only be closed programmatically.
33219 cls               String           A custom CSS class to apply to the message box element
33220 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33221                                    displayed (defaults to 75)
33222 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33223                                    function will be btn (the name of the button that was clicked, if applicable,
33224                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33225                                    Progress and wait dialogs will ignore this option since they do not respond to
33226                                    user actions and can only be closed programmatically, so any required function
33227                                    should be called by the same code after it closes the dialog.
33228 icon              String           A CSS class that provides a background image to be used as an icon for
33229                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33230 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33231 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33232 modal             Boolean          False to allow user interaction with the page while the message box is
33233                                    displayed (defaults to true)
33234 msg               String           A string that will replace the existing message box body text (defaults
33235                                    to the XHTML-compliant non-breaking space character '&#160;')
33236 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33237 progress          Boolean          True to display a progress bar (defaults to false)
33238 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33239 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33240 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33241 title             String           The title text
33242 value             String           The string value to set into the active textbox element if displayed
33243 wait              Boolean          True to display a progress bar (defaults to false)
33244 width             Number           The width of the dialog in pixels
33245 </pre>
33246          *
33247          * Example usage:
33248          * <pre><code>
33249 Roo.Msg.show({
33250    title: 'Address',
33251    msg: 'Please enter your address:',
33252    width: 300,
33253    buttons: Roo.MessageBox.OKCANCEL,
33254    multiline: true,
33255    fn: saveAddress,
33256    animEl: 'addAddressBtn'
33257 });
33258 </code></pre>
33259          * @param {Object} config Configuration options
33260          * @return {Roo.MessageBox} This message box
33261          */
33262         show : function(options)
33263         {
33264             
33265             // this causes nightmares if you show one dialog after another
33266             // especially on callbacks..
33267              
33268             if(this.isVisible()){
33269                 
33270                 this.hide();
33271                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33272                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33273                 Roo.log("New Dialog Message:" +  options.msg )
33274                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33275                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33276                 
33277             }
33278             var d = this.getDialog();
33279             opt = options;
33280             d.setTitle(opt.title || "&#160;");
33281             d.close.setDisplayed(opt.closable !== false);
33282             activeTextEl = textboxEl;
33283             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33284             if(opt.prompt){
33285                 if(opt.multiline){
33286                     textboxEl.hide();
33287                     textareaEl.show();
33288                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33289                         opt.multiline : this.defaultTextHeight);
33290                     activeTextEl = textareaEl;
33291                 }else{
33292                     textboxEl.show();
33293                     textareaEl.hide();
33294                 }
33295             }else{
33296                 textboxEl.hide();
33297                 textareaEl.hide();
33298             }
33299             progressEl.setDisplayed(opt.progress === true);
33300             this.updateProgress(0);
33301             activeTextEl.dom.value = opt.value || "";
33302             if(opt.prompt){
33303                 dlg.setDefaultButton(activeTextEl);
33304             }else{
33305                 var bs = opt.buttons;
33306                 var db = null;
33307                 if(bs && bs.ok){
33308                     db = buttons["ok"];
33309                 }else if(bs && bs.yes){
33310                     db = buttons["yes"];
33311                 }
33312                 dlg.setDefaultButton(db);
33313             }
33314             bwidth = updateButtons(opt.buttons);
33315             this.updateText(opt.msg);
33316             if(opt.cls){
33317                 d.el.addClass(opt.cls);
33318             }
33319             d.proxyDrag = opt.proxyDrag === true;
33320             d.modal = opt.modal !== false;
33321             d.mask = opt.modal !== false ? mask : false;
33322             if(!d.isVisible()){
33323                 // force it to the end of the z-index stack so it gets a cursor in FF
33324                 document.body.appendChild(dlg.el.dom);
33325                 d.animateTarget = null;
33326                 d.show(options.animEl);
33327             }
33328             return this;
33329         },
33330
33331         /**
33332          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33333          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33334          * and closing the message box when the process is complete.
33335          * @param {String} title The title bar text
33336          * @param {String} msg The message box body text
33337          * @return {Roo.MessageBox} This message box
33338          */
33339         progress : function(title, msg){
33340             this.show({
33341                 title : title,
33342                 msg : msg,
33343                 buttons: false,
33344                 progress:true,
33345                 closable:false,
33346                 minWidth: this.minProgressWidth,
33347                 modal : true
33348             });
33349             return this;
33350         },
33351
33352         /**
33353          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33354          * If a callback function is passed it will be called after the user clicks the button, and the
33355          * id of the button that was clicked will be passed as the only parameter to the callback
33356          * (could also be the top-right close button).
33357          * @param {String} title The title bar text
33358          * @param {String} msg The message box body text
33359          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33360          * @param {Object} scope (optional) The scope of the callback function
33361          * @return {Roo.MessageBox} This message box
33362          */
33363         alert : function(title, msg, fn, scope){
33364             this.show({
33365                 title : title,
33366                 msg : msg,
33367                 buttons: this.OK,
33368                 fn: fn,
33369                 scope : scope,
33370                 modal : true
33371             });
33372             return this;
33373         },
33374
33375         /**
33376          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33377          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33378          * You are responsible for closing the message box when the process is complete.
33379          * @param {String} msg The message box body text
33380          * @param {String} title (optional) The title bar text
33381          * @return {Roo.MessageBox} This message box
33382          */
33383         wait : function(msg, title){
33384             this.show({
33385                 title : title,
33386                 msg : msg,
33387                 buttons: false,
33388                 closable:false,
33389                 progress:true,
33390                 modal:true,
33391                 width:300,
33392                 wait:true
33393             });
33394             waitTimer = Roo.TaskMgr.start({
33395                 run: function(i){
33396                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33397                 },
33398                 interval: 1000
33399             });
33400             return this;
33401         },
33402
33403         /**
33404          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33405          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33406          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33407          * @param {String} title The title bar text
33408          * @param {String} msg The message box body text
33409          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33410          * @param {Object} scope (optional) The scope of the callback function
33411          * @return {Roo.MessageBox} This message box
33412          */
33413         confirm : function(title, msg, fn, scope){
33414             this.show({
33415                 title : title,
33416                 msg : msg,
33417                 buttons: this.YESNO,
33418                 fn: fn,
33419                 scope : scope,
33420                 modal : true
33421             });
33422             return this;
33423         },
33424
33425         /**
33426          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33427          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33428          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33429          * (could also be the top-right close button) and the text that was entered will be passed as the two
33430          * parameters to the callback.
33431          * @param {String} title The title bar text
33432          * @param {String} msg The message box body text
33433          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33434          * @param {Object} scope (optional) The scope of the callback function
33435          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33436          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33437          * @return {Roo.MessageBox} This message box
33438          */
33439         prompt : function(title, msg, fn, scope, multiline){
33440             this.show({
33441                 title : title,
33442                 msg : msg,
33443                 buttons: this.OKCANCEL,
33444                 fn: fn,
33445                 minWidth:250,
33446                 scope : scope,
33447                 prompt:true,
33448                 multiline: multiline,
33449                 modal : true
33450             });
33451             return this;
33452         },
33453
33454         /**
33455          * Button config that displays a single OK button
33456          * @type Object
33457          */
33458         OK : {ok:true},
33459         /**
33460          * Button config that displays Yes and No buttons
33461          * @type Object
33462          */
33463         YESNO : {yes:true, no:true},
33464         /**
33465          * Button config that displays OK and Cancel buttons
33466          * @type Object
33467          */
33468         OKCANCEL : {ok:true, cancel:true},
33469         /**
33470          * Button config that displays Yes, No and Cancel buttons
33471          * @type Object
33472          */
33473         YESNOCANCEL : {yes:true, no:true, cancel:true},
33474
33475         /**
33476          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33477          * @type Number
33478          */
33479         defaultTextHeight : 75,
33480         /**
33481          * The maximum width in pixels of the message box (defaults to 600)
33482          * @type Number
33483          */
33484         maxWidth : 600,
33485         /**
33486          * The minimum width in pixels of the message box (defaults to 100)
33487          * @type Number
33488          */
33489         minWidth : 100,
33490         /**
33491          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33492          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33493          * @type Number
33494          */
33495         minProgressWidth : 250,
33496         /**
33497          * An object containing the default button text strings that can be overriden for localized language support.
33498          * Supported properties are: ok, cancel, yes and no.
33499          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33500          * @type Object
33501          */
33502         buttonText : {
33503             ok : "OK",
33504             cancel : "Cancel",
33505             yes : "Yes",
33506             no : "No"
33507         }
33508     };
33509 }();
33510
33511 /**
33512  * Shorthand for {@link Roo.MessageBox}
33513  */
33514 Roo.Msg = Roo.MessageBox;/*
33515  * Based on:
33516  * Ext JS Library 1.1.1
33517  * Copyright(c) 2006-2007, Ext JS, LLC.
33518  *
33519  * Originally Released Under LGPL - original licence link has changed is not relivant.
33520  *
33521  * Fork - LGPL
33522  * <script type="text/javascript">
33523  */
33524 /**
33525  * @class Roo.QuickTips
33526  * Provides attractive and customizable tooltips for any element.
33527  * @singleton
33528  */
33529 Roo.QuickTips = function(){
33530     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33531     var ce, bd, xy, dd;
33532     var visible = false, disabled = true, inited = false;
33533     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33534     
33535     var onOver = function(e){
33536         if(disabled){
33537             return;
33538         }
33539         var t = e.getTarget();
33540         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33541             return;
33542         }
33543         if(ce && t == ce.el){
33544             clearTimeout(hideProc);
33545             return;
33546         }
33547         if(t && tagEls[t.id]){
33548             tagEls[t.id].el = t;
33549             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33550             return;
33551         }
33552         var ttp, et = Roo.fly(t);
33553         var ns = cfg.namespace;
33554         if(tm.interceptTitles && t.title){
33555             ttp = t.title;
33556             t.qtip = ttp;
33557             t.removeAttribute("title");
33558             e.preventDefault();
33559         }else{
33560             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33561         }
33562         if(ttp){
33563             showProc = show.defer(tm.showDelay, tm, [{
33564                 el: t, 
33565                 text: ttp, 
33566                 width: et.getAttributeNS(ns, cfg.width),
33567                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33568                 title: et.getAttributeNS(ns, cfg.title),
33569                     cls: et.getAttributeNS(ns, cfg.cls)
33570             }]);
33571         }
33572     };
33573     
33574     var onOut = function(e){
33575         clearTimeout(showProc);
33576         var t = e.getTarget();
33577         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33578             hideProc = setTimeout(hide, tm.hideDelay);
33579         }
33580     };
33581     
33582     var onMove = function(e){
33583         if(disabled){
33584             return;
33585         }
33586         xy = e.getXY();
33587         xy[1] += 18;
33588         if(tm.trackMouse && ce){
33589             el.setXY(xy);
33590         }
33591     };
33592     
33593     var onDown = function(e){
33594         clearTimeout(showProc);
33595         clearTimeout(hideProc);
33596         if(!e.within(el)){
33597             if(tm.hideOnClick){
33598                 hide();
33599                 tm.disable();
33600                 tm.enable.defer(100, tm);
33601             }
33602         }
33603     };
33604     
33605     var getPad = function(){
33606         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33607     };
33608
33609     var show = function(o){
33610         if(disabled){
33611             return;
33612         }
33613         clearTimeout(dismissProc);
33614         ce = o;
33615         if(removeCls){ // in case manually hidden
33616             el.removeClass(removeCls);
33617             removeCls = null;
33618         }
33619         if(ce.cls){
33620             el.addClass(ce.cls);
33621             removeCls = ce.cls;
33622         }
33623         if(ce.title){
33624             tipTitle.update(ce.title);
33625             tipTitle.show();
33626         }else{
33627             tipTitle.update('');
33628             tipTitle.hide();
33629         }
33630         el.dom.style.width  = tm.maxWidth+'px';
33631         //tipBody.dom.style.width = '';
33632         tipBodyText.update(o.text);
33633         var p = getPad(), w = ce.width;
33634         if(!w){
33635             var td = tipBodyText.dom;
33636             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33637             if(aw > tm.maxWidth){
33638                 w = tm.maxWidth;
33639             }else if(aw < tm.minWidth){
33640                 w = tm.minWidth;
33641             }else{
33642                 w = aw;
33643             }
33644         }
33645         //tipBody.setWidth(w);
33646         el.setWidth(parseInt(w, 10) + p);
33647         if(ce.autoHide === false){
33648             close.setDisplayed(true);
33649             if(dd){
33650                 dd.unlock();
33651             }
33652         }else{
33653             close.setDisplayed(false);
33654             if(dd){
33655                 dd.lock();
33656             }
33657         }
33658         if(xy){
33659             el.avoidY = xy[1]-18;
33660             el.setXY(xy);
33661         }
33662         if(tm.animate){
33663             el.setOpacity(.1);
33664             el.setStyle("visibility", "visible");
33665             el.fadeIn({callback: afterShow});
33666         }else{
33667             afterShow();
33668         }
33669     };
33670     
33671     var afterShow = function(){
33672         if(ce){
33673             el.show();
33674             esc.enable();
33675             if(tm.autoDismiss && ce.autoHide !== false){
33676                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33677             }
33678         }
33679     };
33680     
33681     var hide = function(noanim){
33682         clearTimeout(dismissProc);
33683         clearTimeout(hideProc);
33684         ce = null;
33685         if(el.isVisible()){
33686             esc.disable();
33687             if(noanim !== true && tm.animate){
33688                 el.fadeOut({callback: afterHide});
33689             }else{
33690                 afterHide();
33691             } 
33692         }
33693     };
33694     
33695     var afterHide = function(){
33696         el.hide();
33697         if(removeCls){
33698             el.removeClass(removeCls);
33699             removeCls = null;
33700         }
33701     };
33702     
33703     return {
33704         /**
33705         * @cfg {Number} minWidth
33706         * The minimum width of the quick tip (defaults to 40)
33707         */
33708        minWidth : 40,
33709         /**
33710         * @cfg {Number} maxWidth
33711         * The maximum width of the quick tip (defaults to 300)
33712         */
33713        maxWidth : 300,
33714         /**
33715         * @cfg {Boolean} interceptTitles
33716         * True to automatically use the element's DOM title value if available (defaults to false)
33717         */
33718        interceptTitles : false,
33719         /**
33720         * @cfg {Boolean} trackMouse
33721         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33722         */
33723        trackMouse : false,
33724         /**
33725         * @cfg {Boolean} hideOnClick
33726         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33727         */
33728        hideOnClick : true,
33729         /**
33730         * @cfg {Number} showDelay
33731         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33732         */
33733        showDelay : 500,
33734         /**
33735         * @cfg {Number} hideDelay
33736         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33737         */
33738        hideDelay : 200,
33739         /**
33740         * @cfg {Boolean} autoHide
33741         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33742         * Used in conjunction with hideDelay.
33743         */
33744        autoHide : true,
33745         /**
33746         * @cfg {Boolean}
33747         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
33748         * (defaults to true).  Used in conjunction with autoDismissDelay.
33749         */
33750        autoDismiss : true,
33751         /**
33752         * @cfg {Number}
33753         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
33754         */
33755        autoDismissDelay : 5000,
33756        /**
33757         * @cfg {Boolean} animate
33758         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
33759         */
33760        animate : false,
33761
33762        /**
33763         * @cfg {String} title
33764         * Title text to display (defaults to '').  This can be any valid HTML markup.
33765         */
33766         title: '',
33767        /**
33768         * @cfg {String} text
33769         * Body text to display (defaults to '').  This can be any valid HTML markup.
33770         */
33771         text : '',
33772        /**
33773         * @cfg {String} cls
33774         * A CSS class to apply to the base quick tip element (defaults to '').
33775         */
33776         cls : '',
33777        /**
33778         * @cfg {Number} width
33779         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
33780         * minWidth or maxWidth.
33781         */
33782         width : null,
33783
33784     /**
33785      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
33786      * or display QuickTips in a page.
33787      */
33788        init : function(){
33789           tm = Roo.QuickTips;
33790           cfg = tm.tagConfig;
33791           if(!inited){
33792               if(!Roo.isReady){ // allow calling of init() before onReady
33793                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
33794                   return;
33795               }
33796               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
33797               el.fxDefaults = {stopFx: true};
33798               // maximum custom styling
33799               //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>');
33800               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>');              
33801               tipTitle = el.child('h3');
33802               tipTitle.enableDisplayMode("block");
33803               tipBody = el.child('div.x-tip-bd');
33804               tipBodyText = el.child('div.x-tip-bd-inner');
33805               //bdLeft = el.child('div.x-tip-bd-left');
33806               //bdRight = el.child('div.x-tip-bd-right');
33807               close = el.child('div.x-tip-close');
33808               close.enableDisplayMode("block");
33809               close.on("click", hide);
33810               var d = Roo.get(document);
33811               d.on("mousedown", onDown);
33812               d.on("mouseover", onOver);
33813               d.on("mouseout", onOut);
33814               d.on("mousemove", onMove);
33815               esc = d.addKeyListener(27, hide);
33816               esc.disable();
33817               if(Roo.dd.DD){
33818                   dd = el.initDD("default", null, {
33819                       onDrag : function(){
33820                           el.sync();  
33821                       }
33822                   });
33823                   dd.setHandleElId(tipTitle.id);
33824                   dd.lock();
33825               }
33826               inited = true;
33827           }
33828           this.enable(); 
33829        },
33830
33831     /**
33832      * Configures a new quick tip instance and assigns it to a target element.  The following config options
33833      * are supported:
33834      * <pre>
33835 Property    Type                   Description
33836 ----------  ---------------------  ------------------------------------------------------------------------
33837 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
33838      * </ul>
33839      * @param {Object} config The config object
33840      */
33841        register : function(config){
33842            var cs = config instanceof Array ? config : arguments;
33843            for(var i = 0, len = cs.length; i < len; i++) {
33844                var c = cs[i];
33845                var target = c.target;
33846                if(target){
33847                    if(target instanceof Array){
33848                        for(var j = 0, jlen = target.length; j < jlen; j++){
33849                            tagEls[target[j]] = c;
33850                        }
33851                    }else{
33852                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
33853                    }
33854                }
33855            }
33856        },
33857
33858     /**
33859      * Removes this quick tip from its element and destroys it.
33860      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
33861      */
33862        unregister : function(el){
33863            delete tagEls[Roo.id(el)];
33864        },
33865
33866     /**
33867      * Enable this quick tip.
33868      */
33869        enable : function(){
33870            if(inited && disabled){
33871                locks.pop();
33872                if(locks.length < 1){
33873                    disabled = false;
33874                }
33875            }
33876        },
33877
33878     /**
33879      * Disable this quick tip.
33880      */
33881        disable : function(){
33882           disabled = true;
33883           clearTimeout(showProc);
33884           clearTimeout(hideProc);
33885           clearTimeout(dismissProc);
33886           if(ce){
33887               hide(true);
33888           }
33889           locks.push(1);
33890        },
33891
33892     /**
33893      * Returns true if the quick tip is enabled, else false.
33894      */
33895        isEnabled : function(){
33896             return !disabled;
33897        },
33898
33899         // private
33900        tagConfig : {
33901            namespace : "roo", // was ext?? this may break..
33902            alt_namespace : "ext",
33903            attribute : "qtip",
33904            width : "width",
33905            target : "target",
33906            title : "qtitle",
33907            hide : "hide",
33908            cls : "qclass"
33909        }
33910    };
33911 }();
33912
33913 // backwards compat
33914 Roo.QuickTips.tips = Roo.QuickTips.register;/*
33915  * Based on:
33916  * Ext JS Library 1.1.1
33917  * Copyright(c) 2006-2007, Ext JS, LLC.
33918  *
33919  * Originally Released Under LGPL - original licence link has changed is not relivant.
33920  *
33921  * Fork - LGPL
33922  * <script type="text/javascript">
33923  */
33924  
33925
33926 /**
33927  * @class Roo.tree.TreePanel
33928  * @extends Roo.data.Tree
33929
33930  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
33931  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
33932  * @cfg {Boolean} enableDD true to enable drag and drop
33933  * @cfg {Boolean} enableDrag true to enable just drag
33934  * @cfg {Boolean} enableDrop true to enable just drop
33935  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
33936  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
33937  * @cfg {String} ddGroup The DD group this TreePanel belongs to
33938  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
33939  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
33940  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
33941  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
33942  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
33943  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
33944  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
33945  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
33946  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
33947  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
33948  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
33949  * @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>
33950  * @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>
33951  * 
33952  * @constructor
33953  * @param {String/HTMLElement/Element} el The container element
33954  * @param {Object} config
33955  */
33956 Roo.tree.TreePanel = function(el, config){
33957     var root = false;
33958     var loader = false;
33959     if (config.root) {
33960         root = config.root;
33961         delete config.root;
33962     }
33963     if (config.loader) {
33964         loader = config.loader;
33965         delete config.loader;
33966     }
33967     
33968     Roo.apply(this, config);
33969     Roo.tree.TreePanel.superclass.constructor.call(this);
33970     this.el = Roo.get(el);
33971     this.el.addClass('x-tree');
33972     //console.log(root);
33973     if (root) {
33974         this.setRootNode( Roo.factory(root, Roo.tree));
33975     }
33976     if (loader) {
33977         this.loader = Roo.factory(loader, Roo.tree);
33978     }
33979    /**
33980     * Read-only. The id of the container element becomes this TreePanel's id.
33981     */
33982     this.id = this.el.id;
33983     this.addEvents({
33984         /**
33985         * @event beforeload
33986         * Fires before a node is loaded, return false to cancel
33987         * @param {Node} node The node being loaded
33988         */
33989         "beforeload" : true,
33990         /**
33991         * @event load
33992         * Fires when a node is loaded
33993         * @param {Node} node The node that was loaded
33994         */
33995         "load" : true,
33996         /**
33997         * @event textchange
33998         * Fires when the text for a node is changed
33999         * @param {Node} node The node
34000         * @param {String} text The new text
34001         * @param {String} oldText The old text
34002         */
34003         "textchange" : true,
34004         /**
34005         * @event beforeexpand
34006         * Fires before a node is expanded, return false to cancel.
34007         * @param {Node} node The node
34008         * @param {Boolean} deep
34009         * @param {Boolean} anim
34010         */
34011         "beforeexpand" : true,
34012         /**
34013         * @event beforecollapse
34014         * Fires before a node is collapsed, return false to cancel.
34015         * @param {Node} node The node
34016         * @param {Boolean} deep
34017         * @param {Boolean} anim
34018         */
34019         "beforecollapse" : true,
34020         /**
34021         * @event expand
34022         * Fires when a node is expanded
34023         * @param {Node} node The node
34024         */
34025         "expand" : true,
34026         /**
34027         * @event disabledchange
34028         * Fires when the disabled status of a node changes
34029         * @param {Node} node The node
34030         * @param {Boolean} disabled
34031         */
34032         "disabledchange" : true,
34033         /**
34034         * @event collapse
34035         * Fires when a node is collapsed
34036         * @param {Node} node The node
34037         */
34038         "collapse" : true,
34039         /**
34040         * @event beforeclick
34041         * Fires before click processing on a node. Return false to cancel the default action.
34042         * @param {Node} node The node
34043         * @param {Roo.EventObject} e The event object
34044         */
34045         "beforeclick":true,
34046         /**
34047         * @event checkchange
34048         * Fires when a node with a checkbox's checked property changes
34049         * @param {Node} this This node
34050         * @param {Boolean} checked
34051         */
34052         "checkchange":true,
34053         /**
34054         * @event click
34055         * Fires when a node is clicked
34056         * @param {Node} node The node
34057         * @param {Roo.EventObject} e The event object
34058         */
34059         "click":true,
34060         /**
34061         * @event dblclick
34062         * Fires when a node is double clicked
34063         * @param {Node} node The node
34064         * @param {Roo.EventObject} e The event object
34065         */
34066         "dblclick":true,
34067         /**
34068         * @event contextmenu
34069         * Fires when a node is right clicked
34070         * @param {Node} node The node
34071         * @param {Roo.EventObject} e The event object
34072         */
34073         "contextmenu":true,
34074         /**
34075         * @event beforechildrenrendered
34076         * Fires right before the child nodes for a node are rendered
34077         * @param {Node} node The node
34078         */
34079         "beforechildrenrendered":true,
34080         /**
34081         * @event startdrag
34082         * Fires when a node starts being dragged
34083         * @param {Roo.tree.TreePanel} this
34084         * @param {Roo.tree.TreeNode} node
34085         * @param {event} e The raw browser event
34086         */ 
34087        "startdrag" : true,
34088        /**
34089         * @event enddrag
34090         * Fires when a drag operation is complete
34091         * @param {Roo.tree.TreePanel} this
34092         * @param {Roo.tree.TreeNode} node
34093         * @param {event} e The raw browser event
34094         */
34095        "enddrag" : true,
34096        /**
34097         * @event dragdrop
34098         * Fires when a dragged node is dropped on a valid DD target
34099         * @param {Roo.tree.TreePanel} this
34100         * @param {Roo.tree.TreeNode} node
34101         * @param {DD} dd The dd it was dropped on
34102         * @param {event} e The raw browser event
34103         */
34104        "dragdrop" : true,
34105        /**
34106         * @event beforenodedrop
34107         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34108         * passed to handlers has the following properties:<br />
34109         * <ul style="padding:5px;padding-left:16px;">
34110         * <li>tree - The TreePanel</li>
34111         * <li>target - The node being targeted for the drop</li>
34112         * <li>data - The drag data from the drag source</li>
34113         * <li>point - The point of the drop - append, above or below</li>
34114         * <li>source - The drag source</li>
34115         * <li>rawEvent - Raw mouse event</li>
34116         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34117         * to be inserted by setting them on this object.</li>
34118         * <li>cancel - Set this to true to cancel the drop.</li>
34119         * </ul>
34120         * @param {Object} dropEvent
34121         */
34122        "beforenodedrop" : true,
34123        /**
34124         * @event nodedrop
34125         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34126         * passed to handlers has the following properties:<br />
34127         * <ul style="padding:5px;padding-left:16px;">
34128         * <li>tree - The TreePanel</li>
34129         * <li>target - The node being targeted for the drop</li>
34130         * <li>data - The drag data from the drag source</li>
34131         * <li>point - The point of the drop - append, above or below</li>
34132         * <li>source - The drag source</li>
34133         * <li>rawEvent - Raw mouse event</li>
34134         * <li>dropNode - Dropped node(s).</li>
34135         * </ul>
34136         * @param {Object} dropEvent
34137         */
34138        "nodedrop" : true,
34139         /**
34140         * @event nodedragover
34141         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34142         * passed to handlers has the following properties:<br />
34143         * <ul style="padding:5px;padding-left:16px;">
34144         * <li>tree - The TreePanel</li>
34145         * <li>target - The node being targeted for the drop</li>
34146         * <li>data - The drag data from the drag source</li>
34147         * <li>point - The point of the drop - append, above or below</li>
34148         * <li>source - The drag source</li>
34149         * <li>rawEvent - Raw mouse event</li>
34150         * <li>dropNode - Drop node(s) provided by the source.</li>
34151         * <li>cancel - Set this to true to signal drop not allowed.</li>
34152         * </ul>
34153         * @param {Object} dragOverEvent
34154         */
34155        "nodedragover" : true
34156         
34157     });
34158     if(this.singleExpand){
34159        this.on("beforeexpand", this.restrictExpand, this);
34160     }
34161     if (this.editor) {
34162         this.editor.tree = this;
34163         this.editor = Roo.factory(this.editor, Roo.tree);
34164     }
34165     
34166     if (this.selModel) {
34167         this.selModel = Roo.factory(this.selModel, Roo.tree);
34168     }
34169    
34170 };
34171 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34172     rootVisible : true,
34173     animate: Roo.enableFx,
34174     lines : true,
34175     enableDD : false,
34176     hlDrop : Roo.enableFx,
34177   
34178     renderer: false,
34179     
34180     rendererTip: false,
34181     // private
34182     restrictExpand : function(node){
34183         var p = node.parentNode;
34184         if(p){
34185             if(p.expandedChild && p.expandedChild.parentNode == p){
34186                 p.expandedChild.collapse();
34187             }
34188             p.expandedChild = node;
34189         }
34190     },
34191
34192     // private override
34193     setRootNode : function(node){
34194         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34195         if(!this.rootVisible){
34196             node.ui = new Roo.tree.RootTreeNodeUI(node);
34197         }
34198         return node;
34199     },
34200
34201     /**
34202      * Returns the container element for this TreePanel
34203      */
34204     getEl : function(){
34205         return this.el;
34206     },
34207
34208     /**
34209      * Returns the default TreeLoader for this TreePanel
34210      */
34211     getLoader : function(){
34212         return this.loader;
34213     },
34214
34215     /**
34216      * Expand all nodes
34217      */
34218     expandAll : function(){
34219         this.root.expand(true);
34220     },
34221
34222     /**
34223      * Collapse all nodes
34224      */
34225     collapseAll : function(){
34226         this.root.collapse(true);
34227     },
34228
34229     /**
34230      * Returns the selection model used by this TreePanel
34231      */
34232     getSelectionModel : function(){
34233         if(!this.selModel){
34234             this.selModel = new Roo.tree.DefaultSelectionModel();
34235         }
34236         return this.selModel;
34237     },
34238
34239     /**
34240      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34241      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34242      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34243      * @return {Array}
34244      */
34245     getChecked : function(a, startNode){
34246         startNode = startNode || this.root;
34247         var r = [];
34248         var f = function(){
34249             if(this.attributes.checked){
34250                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34251             }
34252         }
34253         startNode.cascade(f);
34254         return r;
34255     },
34256
34257     /**
34258      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34259      * @param {String} path
34260      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34261      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34262      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34263      */
34264     expandPath : function(path, attr, callback){
34265         attr = attr || "id";
34266         var keys = path.split(this.pathSeparator);
34267         var curNode = this.root;
34268         if(curNode.attributes[attr] != keys[1]){ // invalid root
34269             if(callback){
34270                 callback(false, null);
34271             }
34272             return;
34273         }
34274         var index = 1;
34275         var f = function(){
34276             if(++index == keys.length){
34277                 if(callback){
34278                     callback(true, curNode);
34279                 }
34280                 return;
34281             }
34282             var c = curNode.findChild(attr, keys[index]);
34283             if(!c){
34284                 if(callback){
34285                     callback(false, curNode);
34286                 }
34287                 return;
34288             }
34289             curNode = c;
34290             c.expand(false, false, f);
34291         };
34292         curNode.expand(false, false, f);
34293     },
34294
34295     /**
34296      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34297      * @param {String} path
34298      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34299      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34300      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34301      */
34302     selectPath : function(path, attr, callback){
34303         attr = attr || "id";
34304         var keys = path.split(this.pathSeparator);
34305         var v = keys.pop();
34306         if(keys.length > 0){
34307             var f = function(success, node){
34308                 if(success && node){
34309                     var n = node.findChild(attr, v);
34310                     if(n){
34311                         n.select();
34312                         if(callback){
34313                             callback(true, n);
34314                         }
34315                     }else if(callback){
34316                         callback(false, n);
34317                     }
34318                 }else{
34319                     if(callback){
34320                         callback(false, n);
34321                     }
34322                 }
34323             };
34324             this.expandPath(keys.join(this.pathSeparator), attr, f);
34325         }else{
34326             this.root.select();
34327             if(callback){
34328                 callback(true, this.root);
34329             }
34330         }
34331     },
34332
34333     getTreeEl : function(){
34334         return this.el;
34335     },
34336
34337     /**
34338      * Trigger rendering of this TreePanel
34339      */
34340     render : function(){
34341         if (this.innerCt) {
34342             return this; // stop it rendering more than once!!
34343         }
34344         
34345         this.innerCt = this.el.createChild({tag:"ul",
34346                cls:"x-tree-root-ct " +
34347                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34348
34349         if(this.containerScroll){
34350             Roo.dd.ScrollManager.register(this.el);
34351         }
34352         if((this.enableDD || this.enableDrop) && !this.dropZone){
34353            /**
34354             * The dropZone used by this tree if drop is enabled
34355             * @type Roo.tree.TreeDropZone
34356             */
34357              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34358                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34359            });
34360         }
34361         if((this.enableDD || this.enableDrag) && !this.dragZone){
34362            /**
34363             * The dragZone used by this tree if drag is enabled
34364             * @type Roo.tree.TreeDragZone
34365             */
34366             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34367                ddGroup: this.ddGroup || "TreeDD",
34368                scroll: this.ddScroll
34369            });
34370         }
34371         this.getSelectionModel().init(this);
34372         if (!this.root) {
34373             Roo.log("ROOT not set in tree");
34374             return this;
34375         }
34376         this.root.render();
34377         if(!this.rootVisible){
34378             this.root.renderChildren();
34379         }
34380         return this;
34381     }
34382 });/*
34383  * Based on:
34384  * Ext JS Library 1.1.1
34385  * Copyright(c) 2006-2007, Ext JS, LLC.
34386  *
34387  * Originally Released Under LGPL - original licence link has changed is not relivant.
34388  *
34389  * Fork - LGPL
34390  * <script type="text/javascript">
34391  */
34392  
34393
34394 /**
34395  * @class Roo.tree.DefaultSelectionModel
34396  * @extends Roo.util.Observable
34397  * The default single selection for a TreePanel.
34398  * @param {Object} cfg Configuration
34399  */
34400 Roo.tree.DefaultSelectionModel = function(cfg){
34401    this.selNode = null;
34402    
34403    
34404    
34405    this.addEvents({
34406        /**
34407         * @event selectionchange
34408         * Fires when the selected node changes
34409         * @param {DefaultSelectionModel} this
34410         * @param {TreeNode} node the new selection
34411         */
34412        "selectionchange" : true,
34413
34414        /**
34415         * @event beforeselect
34416         * Fires before the selected node changes, return false to cancel the change
34417         * @param {DefaultSelectionModel} this
34418         * @param {TreeNode} node the new selection
34419         * @param {TreeNode} node the old selection
34420         */
34421        "beforeselect" : true
34422    });
34423    
34424     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34425 };
34426
34427 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34428     init : function(tree){
34429         this.tree = tree;
34430         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34431         tree.on("click", this.onNodeClick, this);
34432     },
34433     
34434     onNodeClick : function(node, e){
34435         if (e.ctrlKey && this.selNode == node)  {
34436             this.unselect(node);
34437             return;
34438         }
34439         this.select(node);
34440     },
34441     
34442     /**
34443      * Select a node.
34444      * @param {TreeNode} node The node to select
34445      * @return {TreeNode} The selected node
34446      */
34447     select : function(node){
34448         var last = this.selNode;
34449         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34450             if(last){
34451                 last.ui.onSelectedChange(false);
34452             }
34453             this.selNode = node;
34454             node.ui.onSelectedChange(true);
34455             this.fireEvent("selectionchange", this, node, last);
34456         }
34457         return node;
34458     },
34459     
34460     /**
34461      * Deselect a node.
34462      * @param {TreeNode} node The node to unselect
34463      */
34464     unselect : function(node){
34465         if(this.selNode == node){
34466             this.clearSelections();
34467         }    
34468     },
34469     
34470     /**
34471      * Clear all selections
34472      */
34473     clearSelections : function(){
34474         var n = this.selNode;
34475         if(n){
34476             n.ui.onSelectedChange(false);
34477             this.selNode = null;
34478             this.fireEvent("selectionchange", this, null);
34479         }
34480         return n;
34481     },
34482     
34483     /**
34484      * Get the selected node
34485      * @return {TreeNode} The selected node
34486      */
34487     getSelectedNode : function(){
34488         return this.selNode;    
34489     },
34490     
34491     /**
34492      * Returns true if the node is selected
34493      * @param {TreeNode} node The node to check
34494      * @return {Boolean}
34495      */
34496     isSelected : function(node){
34497         return this.selNode == node;  
34498     },
34499
34500     /**
34501      * Selects the node above the selected node in the tree, intelligently walking the nodes
34502      * @return TreeNode The new selection
34503      */
34504     selectPrevious : function(){
34505         var s = this.selNode || this.lastSelNode;
34506         if(!s){
34507             return null;
34508         }
34509         var ps = s.previousSibling;
34510         if(ps){
34511             if(!ps.isExpanded() || ps.childNodes.length < 1){
34512                 return this.select(ps);
34513             } else{
34514                 var lc = ps.lastChild;
34515                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34516                     lc = lc.lastChild;
34517                 }
34518                 return this.select(lc);
34519             }
34520         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34521             return this.select(s.parentNode);
34522         }
34523         return null;
34524     },
34525
34526     /**
34527      * Selects the node above the selected node in the tree, intelligently walking the nodes
34528      * @return TreeNode The new selection
34529      */
34530     selectNext : function(){
34531         var s = this.selNode || this.lastSelNode;
34532         if(!s){
34533             return null;
34534         }
34535         if(s.firstChild && s.isExpanded()){
34536              return this.select(s.firstChild);
34537          }else if(s.nextSibling){
34538              return this.select(s.nextSibling);
34539          }else if(s.parentNode){
34540             var newS = null;
34541             s.parentNode.bubble(function(){
34542                 if(this.nextSibling){
34543                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34544                     return false;
34545                 }
34546             });
34547             return newS;
34548          }
34549         return null;
34550     },
34551
34552     onKeyDown : function(e){
34553         var s = this.selNode || this.lastSelNode;
34554         // undesirable, but required
34555         var sm = this;
34556         if(!s){
34557             return;
34558         }
34559         var k = e.getKey();
34560         switch(k){
34561              case e.DOWN:
34562                  e.stopEvent();
34563                  this.selectNext();
34564              break;
34565              case e.UP:
34566                  e.stopEvent();
34567                  this.selectPrevious();
34568              break;
34569              case e.RIGHT:
34570                  e.preventDefault();
34571                  if(s.hasChildNodes()){
34572                      if(!s.isExpanded()){
34573                          s.expand();
34574                      }else if(s.firstChild){
34575                          this.select(s.firstChild, e);
34576                      }
34577                  }
34578              break;
34579              case e.LEFT:
34580                  e.preventDefault();
34581                  if(s.hasChildNodes() && s.isExpanded()){
34582                      s.collapse();
34583                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34584                      this.select(s.parentNode, e);
34585                  }
34586              break;
34587         };
34588     }
34589 });
34590
34591 /**
34592  * @class Roo.tree.MultiSelectionModel
34593  * @extends Roo.util.Observable
34594  * Multi selection for a TreePanel.
34595  * @param {Object} cfg Configuration
34596  */
34597 Roo.tree.MultiSelectionModel = function(){
34598    this.selNodes = [];
34599    this.selMap = {};
34600    this.addEvents({
34601        /**
34602         * @event selectionchange
34603         * Fires when the selected nodes change
34604         * @param {MultiSelectionModel} this
34605         * @param {Array} nodes Array of the selected nodes
34606         */
34607        "selectionchange" : true
34608    });
34609    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34610    
34611 };
34612
34613 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34614     init : function(tree){
34615         this.tree = tree;
34616         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34617         tree.on("click", this.onNodeClick, this);
34618     },
34619     
34620     onNodeClick : function(node, e){
34621         this.select(node, e, e.ctrlKey);
34622     },
34623     
34624     /**
34625      * Select a node.
34626      * @param {TreeNode} node The node to select
34627      * @param {EventObject} e (optional) An event associated with the selection
34628      * @param {Boolean} keepExisting True to retain existing selections
34629      * @return {TreeNode} The selected node
34630      */
34631     select : function(node, e, keepExisting){
34632         if(keepExisting !== true){
34633             this.clearSelections(true);
34634         }
34635         if(this.isSelected(node)){
34636             this.lastSelNode = node;
34637             return node;
34638         }
34639         this.selNodes.push(node);
34640         this.selMap[node.id] = node;
34641         this.lastSelNode = node;
34642         node.ui.onSelectedChange(true);
34643         this.fireEvent("selectionchange", this, this.selNodes);
34644         return node;
34645     },
34646     
34647     /**
34648      * Deselect a node.
34649      * @param {TreeNode} node The node to unselect
34650      */
34651     unselect : function(node){
34652         if(this.selMap[node.id]){
34653             node.ui.onSelectedChange(false);
34654             var sn = this.selNodes;
34655             var index = -1;
34656             if(sn.indexOf){
34657                 index = sn.indexOf(node);
34658             }else{
34659                 for(var i = 0, len = sn.length; i < len; i++){
34660                     if(sn[i] == node){
34661                         index = i;
34662                         break;
34663                     }
34664                 }
34665             }
34666             if(index != -1){
34667                 this.selNodes.splice(index, 1);
34668             }
34669             delete this.selMap[node.id];
34670             this.fireEvent("selectionchange", this, this.selNodes);
34671         }
34672     },
34673     
34674     /**
34675      * Clear all selections
34676      */
34677     clearSelections : function(suppressEvent){
34678         var sn = this.selNodes;
34679         if(sn.length > 0){
34680             for(var i = 0, len = sn.length; i < len; i++){
34681                 sn[i].ui.onSelectedChange(false);
34682             }
34683             this.selNodes = [];
34684             this.selMap = {};
34685             if(suppressEvent !== true){
34686                 this.fireEvent("selectionchange", this, this.selNodes);
34687             }
34688         }
34689     },
34690     
34691     /**
34692      * Returns true if the node is selected
34693      * @param {TreeNode} node The node to check
34694      * @return {Boolean}
34695      */
34696     isSelected : function(node){
34697         return this.selMap[node.id] ? true : false;  
34698     },
34699     
34700     /**
34701      * Returns an array of the selected nodes
34702      * @return {Array}
34703      */
34704     getSelectedNodes : function(){
34705         return this.selNodes;    
34706     },
34707
34708     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34709
34710     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34711
34712     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34713 });/*
34714  * Based on:
34715  * Ext JS Library 1.1.1
34716  * Copyright(c) 2006-2007, Ext JS, LLC.
34717  *
34718  * Originally Released Under LGPL - original licence link has changed is not relivant.
34719  *
34720  * Fork - LGPL
34721  * <script type="text/javascript">
34722  */
34723  
34724 /**
34725  * @class Roo.tree.TreeNode
34726  * @extends Roo.data.Node
34727  * @cfg {String} text The text for this node
34728  * @cfg {Boolean} expanded true to start the node expanded
34729  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34730  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34731  * @cfg {Boolean} disabled true to start the node disabled
34732  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34733  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
34734  * @cfg {String} cls A css class to be added to the node
34735  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34736  * @cfg {String} href URL of the link used for the node (defaults to #)
34737  * @cfg {String} hrefTarget target frame for the link
34738  * @cfg {String} qtip An Ext QuickTip for the node
34739  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
34740  * @cfg {Boolean} singleClickExpand True for single click expand on this node
34741  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
34742  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
34743  * (defaults to undefined with no checkbox rendered)
34744  * @constructor
34745  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
34746  */
34747 Roo.tree.TreeNode = function(attributes){
34748     attributes = attributes || {};
34749     if(typeof attributes == "string"){
34750         attributes = {text: attributes};
34751     }
34752     this.childrenRendered = false;
34753     this.rendered = false;
34754     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
34755     this.expanded = attributes.expanded === true;
34756     this.isTarget = attributes.isTarget !== false;
34757     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
34758     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
34759
34760     /**
34761      * Read-only. The text for this node. To change it use setText().
34762      * @type String
34763      */
34764     this.text = attributes.text;
34765     /**
34766      * True if this node is disabled.
34767      * @type Boolean
34768      */
34769     this.disabled = attributes.disabled === true;
34770
34771     this.addEvents({
34772         /**
34773         * @event textchange
34774         * Fires when the text for this node is changed
34775         * @param {Node} this This node
34776         * @param {String} text The new text
34777         * @param {String} oldText The old text
34778         */
34779         "textchange" : true,
34780         /**
34781         * @event beforeexpand
34782         * Fires before this node is expanded, return false to cancel.
34783         * @param {Node} this This node
34784         * @param {Boolean} deep
34785         * @param {Boolean} anim
34786         */
34787         "beforeexpand" : true,
34788         /**
34789         * @event beforecollapse
34790         * Fires before this node is collapsed, return false to cancel.
34791         * @param {Node} this This node
34792         * @param {Boolean} deep
34793         * @param {Boolean} anim
34794         */
34795         "beforecollapse" : true,
34796         /**
34797         * @event expand
34798         * Fires when this node is expanded
34799         * @param {Node} this This node
34800         */
34801         "expand" : true,
34802         /**
34803         * @event disabledchange
34804         * Fires when the disabled status of this node changes
34805         * @param {Node} this This node
34806         * @param {Boolean} disabled
34807         */
34808         "disabledchange" : true,
34809         /**
34810         * @event collapse
34811         * Fires when this node is collapsed
34812         * @param {Node} this This node
34813         */
34814         "collapse" : true,
34815         /**
34816         * @event beforeclick
34817         * Fires before click processing. Return false to cancel the default action.
34818         * @param {Node} this This node
34819         * @param {Roo.EventObject} e The event object
34820         */
34821         "beforeclick":true,
34822         /**
34823         * @event checkchange
34824         * Fires when a node with a checkbox's checked property changes
34825         * @param {Node} this This node
34826         * @param {Boolean} checked
34827         */
34828         "checkchange":true,
34829         /**
34830         * @event click
34831         * Fires when this node is clicked
34832         * @param {Node} this This node
34833         * @param {Roo.EventObject} e The event object
34834         */
34835         "click":true,
34836         /**
34837         * @event dblclick
34838         * Fires when this node is double clicked
34839         * @param {Node} this This node
34840         * @param {Roo.EventObject} e The event object
34841         */
34842         "dblclick":true,
34843         /**
34844         * @event contextmenu
34845         * Fires when this node is right clicked
34846         * @param {Node} this This node
34847         * @param {Roo.EventObject} e The event object
34848         */
34849         "contextmenu":true,
34850         /**
34851         * @event beforechildrenrendered
34852         * Fires right before the child nodes for this node are rendered
34853         * @param {Node} this This node
34854         */
34855         "beforechildrenrendered":true
34856     });
34857
34858     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
34859
34860     /**
34861      * Read-only. The UI for this node
34862      * @type TreeNodeUI
34863      */
34864     this.ui = new uiClass(this);
34865     
34866     // finally support items[]
34867     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
34868         return;
34869     }
34870     
34871     
34872     Roo.each(this.attributes.items, function(c) {
34873         this.appendChild(Roo.factory(c,Roo.Tree));
34874     }, this);
34875     delete this.attributes.items;
34876     
34877     
34878     
34879 };
34880 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
34881     preventHScroll: true,
34882     /**
34883      * Returns true if this node is expanded
34884      * @return {Boolean}
34885      */
34886     isExpanded : function(){
34887         return this.expanded;
34888     },
34889
34890     /**
34891      * Returns the UI object for this node
34892      * @return {TreeNodeUI}
34893      */
34894     getUI : function(){
34895         return this.ui;
34896     },
34897
34898     // private override
34899     setFirstChild : function(node){
34900         var of = this.firstChild;
34901         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
34902         if(this.childrenRendered && of && node != of){
34903             of.renderIndent(true, true);
34904         }
34905         if(this.rendered){
34906             this.renderIndent(true, true);
34907         }
34908     },
34909
34910     // private override
34911     setLastChild : function(node){
34912         var ol = this.lastChild;
34913         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
34914         if(this.childrenRendered && ol && node != ol){
34915             ol.renderIndent(true, true);
34916         }
34917         if(this.rendered){
34918             this.renderIndent(true, true);
34919         }
34920     },
34921
34922     // these methods are overridden to provide lazy rendering support
34923     // private override
34924     appendChild : function()
34925     {
34926         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
34927         if(node && this.childrenRendered){
34928             node.render();
34929         }
34930         this.ui.updateExpandIcon();
34931         return node;
34932     },
34933
34934     // private override
34935     removeChild : function(node){
34936         this.ownerTree.getSelectionModel().unselect(node);
34937         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
34938         // if it's been rendered remove dom node
34939         if(this.childrenRendered){
34940             node.ui.remove();
34941         }
34942         if(this.childNodes.length < 1){
34943             this.collapse(false, false);
34944         }else{
34945             this.ui.updateExpandIcon();
34946         }
34947         if(!this.firstChild) {
34948             this.childrenRendered = false;
34949         }
34950         return node;
34951     },
34952
34953     // private override
34954     insertBefore : function(node, refNode){
34955         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
34956         if(newNode && refNode && this.childrenRendered){
34957             node.render();
34958         }
34959         this.ui.updateExpandIcon();
34960         return newNode;
34961     },
34962
34963     /**
34964      * Sets the text for this node
34965      * @param {String} text
34966      */
34967     setText : function(text){
34968         var oldText = this.text;
34969         this.text = text;
34970         this.attributes.text = text;
34971         if(this.rendered){ // event without subscribing
34972             this.ui.onTextChange(this, text, oldText);
34973         }
34974         this.fireEvent("textchange", this, text, oldText);
34975     },
34976
34977     /**
34978      * Triggers selection of this node
34979      */
34980     select : function(){
34981         this.getOwnerTree().getSelectionModel().select(this);
34982     },
34983
34984     /**
34985      * Triggers deselection of this node
34986      */
34987     unselect : function(){
34988         this.getOwnerTree().getSelectionModel().unselect(this);
34989     },
34990
34991     /**
34992      * Returns true if this node is selected
34993      * @return {Boolean}
34994      */
34995     isSelected : function(){
34996         return this.getOwnerTree().getSelectionModel().isSelected(this);
34997     },
34998
34999     /**
35000      * Expand this node.
35001      * @param {Boolean} deep (optional) True to expand all children as well
35002      * @param {Boolean} anim (optional) false to cancel the default animation
35003      * @param {Function} callback (optional) A callback to be called when
35004      * expanding this node completes (does not wait for deep expand to complete).
35005      * Called with 1 parameter, this node.
35006      */
35007     expand : function(deep, anim, callback){
35008         if(!this.expanded){
35009             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35010                 return;
35011             }
35012             if(!this.childrenRendered){
35013                 this.renderChildren();
35014             }
35015             this.expanded = true;
35016             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
35017                 this.ui.animExpand(function(){
35018                     this.fireEvent("expand", this);
35019                     if(typeof callback == "function"){
35020                         callback(this);
35021                     }
35022                     if(deep === true){
35023                         this.expandChildNodes(true);
35024                     }
35025                 }.createDelegate(this));
35026                 return;
35027             }else{
35028                 this.ui.expand();
35029                 this.fireEvent("expand", this);
35030                 if(typeof callback == "function"){
35031                     callback(this);
35032                 }
35033             }
35034         }else{
35035            if(typeof callback == "function"){
35036                callback(this);
35037            }
35038         }
35039         if(deep === true){
35040             this.expandChildNodes(true);
35041         }
35042     },
35043
35044     isHiddenRoot : function(){
35045         return this.isRoot && !this.getOwnerTree().rootVisible;
35046     },
35047
35048     /**
35049      * Collapse this node.
35050      * @param {Boolean} deep (optional) True to collapse all children as well
35051      * @param {Boolean} anim (optional) false to cancel the default animation
35052      */
35053     collapse : function(deep, anim){
35054         if(this.expanded && !this.isHiddenRoot()){
35055             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35056                 return;
35057             }
35058             this.expanded = false;
35059             if((this.getOwnerTree().animate && anim !== false) || anim){
35060                 this.ui.animCollapse(function(){
35061                     this.fireEvent("collapse", this);
35062                     if(deep === true){
35063                         this.collapseChildNodes(true);
35064                     }
35065                 }.createDelegate(this));
35066                 return;
35067             }else{
35068                 this.ui.collapse();
35069                 this.fireEvent("collapse", this);
35070             }
35071         }
35072         if(deep === true){
35073             var cs = this.childNodes;
35074             for(var i = 0, len = cs.length; i < len; i++) {
35075                 cs[i].collapse(true, false);
35076             }
35077         }
35078     },
35079
35080     // private
35081     delayedExpand : function(delay){
35082         if(!this.expandProcId){
35083             this.expandProcId = this.expand.defer(delay, this);
35084         }
35085     },
35086
35087     // private
35088     cancelExpand : function(){
35089         if(this.expandProcId){
35090             clearTimeout(this.expandProcId);
35091         }
35092         this.expandProcId = false;
35093     },
35094
35095     /**
35096      * Toggles expanded/collapsed state of the node
35097      */
35098     toggle : function(){
35099         if(this.expanded){
35100             this.collapse();
35101         }else{
35102             this.expand();
35103         }
35104     },
35105
35106     /**
35107      * Ensures all parent nodes are expanded
35108      */
35109     ensureVisible : function(callback){
35110         var tree = this.getOwnerTree();
35111         tree.expandPath(this.parentNode.getPath(), false, function(){
35112             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35113             Roo.callback(callback);
35114         }.createDelegate(this));
35115     },
35116
35117     /**
35118      * Expand all child nodes
35119      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35120      */
35121     expandChildNodes : function(deep){
35122         var cs = this.childNodes;
35123         for(var i = 0, len = cs.length; i < len; i++) {
35124                 cs[i].expand(deep);
35125         }
35126     },
35127
35128     /**
35129      * Collapse all child nodes
35130      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35131      */
35132     collapseChildNodes : function(deep){
35133         var cs = this.childNodes;
35134         for(var i = 0, len = cs.length; i < len; i++) {
35135                 cs[i].collapse(deep);
35136         }
35137     },
35138
35139     /**
35140      * Disables this node
35141      */
35142     disable : function(){
35143         this.disabled = true;
35144         this.unselect();
35145         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35146             this.ui.onDisableChange(this, true);
35147         }
35148         this.fireEvent("disabledchange", this, true);
35149     },
35150
35151     /**
35152      * Enables this node
35153      */
35154     enable : function(){
35155         this.disabled = false;
35156         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35157             this.ui.onDisableChange(this, false);
35158         }
35159         this.fireEvent("disabledchange", this, false);
35160     },
35161
35162     // private
35163     renderChildren : function(suppressEvent){
35164         if(suppressEvent !== false){
35165             this.fireEvent("beforechildrenrendered", this);
35166         }
35167         var cs = this.childNodes;
35168         for(var i = 0, len = cs.length; i < len; i++){
35169             cs[i].render(true);
35170         }
35171         this.childrenRendered = true;
35172     },
35173
35174     // private
35175     sort : function(fn, scope){
35176         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35177         if(this.childrenRendered){
35178             var cs = this.childNodes;
35179             for(var i = 0, len = cs.length; i < len; i++){
35180                 cs[i].render(true);
35181             }
35182         }
35183     },
35184
35185     // private
35186     render : function(bulkRender){
35187         this.ui.render(bulkRender);
35188         if(!this.rendered){
35189             this.rendered = true;
35190             if(this.expanded){
35191                 this.expanded = false;
35192                 this.expand(false, false);
35193             }
35194         }
35195     },
35196
35197     // private
35198     renderIndent : function(deep, refresh){
35199         if(refresh){
35200             this.ui.childIndent = null;
35201         }
35202         this.ui.renderIndent();
35203         if(deep === true && this.childrenRendered){
35204             var cs = this.childNodes;
35205             for(var i = 0, len = cs.length; i < len; i++){
35206                 cs[i].renderIndent(true, refresh);
35207             }
35208         }
35209     }
35210 });/*
35211  * Based on:
35212  * Ext JS Library 1.1.1
35213  * Copyright(c) 2006-2007, Ext JS, LLC.
35214  *
35215  * Originally Released Under LGPL - original licence link has changed is not relivant.
35216  *
35217  * Fork - LGPL
35218  * <script type="text/javascript">
35219  */
35220  
35221 /**
35222  * @class Roo.tree.AsyncTreeNode
35223  * @extends Roo.tree.TreeNode
35224  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35225  * @constructor
35226  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35227  */
35228  Roo.tree.AsyncTreeNode = function(config){
35229     this.loaded = false;
35230     this.loading = false;
35231     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35232     /**
35233     * @event beforeload
35234     * Fires before this node is loaded, return false to cancel
35235     * @param {Node} this This node
35236     */
35237     this.addEvents({'beforeload':true, 'load': true});
35238     /**
35239     * @event load
35240     * Fires when this node is loaded
35241     * @param {Node} this This node
35242     */
35243     /**
35244      * The loader used by this node (defaults to using the tree's defined loader)
35245      * @type TreeLoader
35246      * @property loader
35247      */
35248 };
35249 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35250     expand : function(deep, anim, callback){
35251         if(this.loading){ // if an async load is already running, waiting til it's done
35252             var timer;
35253             var f = function(){
35254                 if(!this.loading){ // done loading
35255                     clearInterval(timer);
35256                     this.expand(deep, anim, callback);
35257                 }
35258             }.createDelegate(this);
35259             timer = setInterval(f, 200);
35260             return;
35261         }
35262         if(!this.loaded){
35263             if(this.fireEvent("beforeload", this) === false){
35264                 return;
35265             }
35266             this.loading = true;
35267             this.ui.beforeLoad(this);
35268             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35269             if(loader){
35270                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35271                 return;
35272             }
35273         }
35274         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35275     },
35276     
35277     /**
35278      * Returns true if this node is currently loading
35279      * @return {Boolean}
35280      */
35281     isLoading : function(){
35282         return this.loading;  
35283     },
35284     
35285     loadComplete : function(deep, anim, callback){
35286         this.loading = false;
35287         this.loaded = true;
35288         this.ui.afterLoad(this);
35289         this.fireEvent("load", this);
35290         this.expand(deep, anim, callback);
35291     },
35292     
35293     /**
35294      * Returns true if this node has been loaded
35295      * @return {Boolean}
35296      */
35297     isLoaded : function(){
35298         return this.loaded;
35299     },
35300     
35301     hasChildNodes : function(){
35302         if(!this.isLeaf() && !this.loaded){
35303             return true;
35304         }else{
35305             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35306         }
35307     },
35308
35309     /**
35310      * Trigger a reload for this node
35311      * @param {Function} callback
35312      */
35313     reload : function(callback){
35314         this.collapse(false, false);
35315         while(this.firstChild){
35316             this.removeChild(this.firstChild);
35317         }
35318         this.childrenRendered = false;
35319         this.loaded = false;
35320         if(this.isHiddenRoot()){
35321             this.expanded = false;
35322         }
35323         this.expand(false, false, callback);
35324     }
35325 });/*
35326  * Based on:
35327  * Ext JS Library 1.1.1
35328  * Copyright(c) 2006-2007, Ext JS, LLC.
35329  *
35330  * Originally Released Under LGPL - original licence link has changed is not relivant.
35331  *
35332  * Fork - LGPL
35333  * <script type="text/javascript">
35334  */
35335  
35336 /**
35337  * @class Roo.tree.TreeNodeUI
35338  * @constructor
35339  * @param {Object} node The node to render
35340  * The TreeNode UI implementation is separate from the
35341  * tree implementation. Unless you are customizing the tree UI,
35342  * you should never have to use this directly.
35343  */
35344 Roo.tree.TreeNodeUI = function(node){
35345     this.node = node;
35346     this.rendered = false;
35347     this.animating = false;
35348     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35349 };
35350
35351 Roo.tree.TreeNodeUI.prototype = {
35352     removeChild : function(node){
35353         if(this.rendered){
35354             this.ctNode.removeChild(node.ui.getEl());
35355         }
35356     },
35357
35358     beforeLoad : function(){
35359          this.addClass("x-tree-node-loading");
35360     },
35361
35362     afterLoad : function(){
35363          this.removeClass("x-tree-node-loading");
35364     },
35365
35366     onTextChange : function(node, text, oldText){
35367         if(this.rendered){
35368             this.textNode.innerHTML = text;
35369         }
35370     },
35371
35372     onDisableChange : function(node, state){
35373         this.disabled = state;
35374         if(state){
35375             this.addClass("x-tree-node-disabled");
35376         }else{
35377             this.removeClass("x-tree-node-disabled");
35378         }
35379     },
35380
35381     onSelectedChange : function(state){
35382         if(state){
35383             this.focus();
35384             this.addClass("x-tree-selected");
35385         }else{
35386             //this.blur();
35387             this.removeClass("x-tree-selected");
35388         }
35389     },
35390
35391     onMove : function(tree, node, oldParent, newParent, index, refNode){
35392         this.childIndent = null;
35393         if(this.rendered){
35394             var targetNode = newParent.ui.getContainer();
35395             if(!targetNode){//target not rendered
35396                 this.holder = document.createElement("div");
35397                 this.holder.appendChild(this.wrap);
35398                 return;
35399             }
35400             var insertBefore = refNode ? refNode.ui.getEl() : null;
35401             if(insertBefore){
35402                 targetNode.insertBefore(this.wrap, insertBefore);
35403             }else{
35404                 targetNode.appendChild(this.wrap);
35405             }
35406             this.node.renderIndent(true);
35407         }
35408     },
35409
35410     addClass : function(cls){
35411         if(this.elNode){
35412             Roo.fly(this.elNode).addClass(cls);
35413         }
35414     },
35415
35416     removeClass : function(cls){
35417         if(this.elNode){
35418             Roo.fly(this.elNode).removeClass(cls);
35419         }
35420     },
35421
35422     remove : function(){
35423         if(this.rendered){
35424             this.holder = document.createElement("div");
35425             this.holder.appendChild(this.wrap);
35426         }
35427     },
35428
35429     fireEvent : function(){
35430         return this.node.fireEvent.apply(this.node, arguments);
35431     },
35432
35433     initEvents : function(){
35434         this.node.on("move", this.onMove, this);
35435         var E = Roo.EventManager;
35436         var a = this.anchor;
35437
35438         var el = Roo.fly(a, '_treeui');
35439
35440         if(Roo.isOpera){ // opera render bug ignores the CSS
35441             el.setStyle("text-decoration", "none");
35442         }
35443
35444         el.on("click", this.onClick, this);
35445         el.on("dblclick", this.onDblClick, this);
35446
35447         if(this.checkbox){
35448             Roo.EventManager.on(this.checkbox,
35449                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35450         }
35451
35452         el.on("contextmenu", this.onContextMenu, this);
35453
35454         var icon = Roo.fly(this.iconNode);
35455         icon.on("click", this.onClick, this);
35456         icon.on("dblclick", this.onDblClick, this);
35457         icon.on("contextmenu", this.onContextMenu, this);
35458         E.on(this.ecNode, "click", this.ecClick, this, true);
35459
35460         if(this.node.disabled){
35461             this.addClass("x-tree-node-disabled");
35462         }
35463         if(this.node.hidden){
35464             this.addClass("x-tree-node-disabled");
35465         }
35466         var ot = this.node.getOwnerTree();
35467         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
35468         if(dd && (!this.node.isRoot || ot.rootVisible)){
35469             Roo.dd.Registry.register(this.elNode, {
35470                 node: this.node,
35471                 handles: this.getDDHandles(),
35472                 isHandle: false
35473             });
35474         }
35475     },
35476
35477     getDDHandles : function(){
35478         return [this.iconNode, this.textNode];
35479     },
35480
35481     hide : function(){
35482         if(this.rendered){
35483             this.wrap.style.display = "none";
35484         }
35485     },
35486
35487     show : function(){
35488         if(this.rendered){
35489             this.wrap.style.display = "";
35490         }
35491     },
35492
35493     onContextMenu : function(e){
35494         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35495             e.preventDefault();
35496             this.focus();
35497             this.fireEvent("contextmenu", this.node, e);
35498         }
35499     },
35500
35501     onClick : function(e){
35502         if(this.dropping){
35503             e.stopEvent();
35504             return;
35505         }
35506         if(this.fireEvent("beforeclick", this.node, e) !== false){
35507             if(!this.disabled && this.node.attributes.href){
35508                 this.fireEvent("click", this.node, e);
35509                 return;
35510             }
35511             e.preventDefault();
35512             if(this.disabled){
35513                 return;
35514             }
35515
35516             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35517                 this.node.toggle();
35518             }
35519
35520             this.fireEvent("click", this.node, e);
35521         }else{
35522             e.stopEvent();
35523         }
35524     },
35525
35526     onDblClick : function(e){
35527         e.preventDefault();
35528         if(this.disabled){
35529             return;
35530         }
35531         if(this.checkbox){
35532             this.toggleCheck();
35533         }
35534         if(!this.animating && this.node.hasChildNodes()){
35535             this.node.toggle();
35536         }
35537         this.fireEvent("dblclick", this.node, e);
35538     },
35539
35540     onCheckChange : function(){
35541         var checked = this.checkbox.checked;
35542         this.node.attributes.checked = checked;
35543         this.fireEvent('checkchange', this.node, checked);
35544     },
35545
35546     ecClick : function(e){
35547         if(!this.animating && this.node.hasChildNodes()){
35548             this.node.toggle();
35549         }
35550     },
35551
35552     startDrop : function(){
35553         this.dropping = true;
35554     },
35555
35556     // delayed drop so the click event doesn't get fired on a drop
35557     endDrop : function(){
35558        setTimeout(function(){
35559            this.dropping = false;
35560        }.createDelegate(this), 50);
35561     },
35562
35563     expand : function(){
35564         this.updateExpandIcon();
35565         this.ctNode.style.display = "";
35566     },
35567
35568     focus : function(){
35569         if(!this.node.preventHScroll){
35570             try{this.anchor.focus();
35571             }catch(e){}
35572         }else if(!Roo.isIE){
35573             try{
35574                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35575                 var l = noscroll.scrollLeft;
35576                 this.anchor.focus();
35577                 noscroll.scrollLeft = l;
35578             }catch(e){}
35579         }
35580     },
35581
35582     toggleCheck : function(value){
35583         var cb = this.checkbox;
35584         if(cb){
35585             cb.checked = (value === undefined ? !cb.checked : value);
35586         }
35587     },
35588
35589     blur : function(){
35590         try{
35591             this.anchor.blur();
35592         }catch(e){}
35593     },
35594
35595     animExpand : function(callback){
35596         var ct = Roo.get(this.ctNode);
35597         ct.stopFx();
35598         if(!this.node.hasChildNodes()){
35599             this.updateExpandIcon();
35600             this.ctNode.style.display = "";
35601             Roo.callback(callback);
35602             return;
35603         }
35604         this.animating = true;
35605         this.updateExpandIcon();
35606
35607         ct.slideIn('t', {
35608            callback : function(){
35609                this.animating = false;
35610                Roo.callback(callback);
35611             },
35612             scope: this,
35613             duration: this.node.ownerTree.duration || .25
35614         });
35615     },
35616
35617     highlight : function(){
35618         var tree = this.node.getOwnerTree();
35619         Roo.fly(this.wrap).highlight(
35620             tree.hlColor || "C3DAF9",
35621             {endColor: tree.hlBaseColor}
35622         );
35623     },
35624
35625     collapse : function(){
35626         this.updateExpandIcon();
35627         this.ctNode.style.display = "none";
35628     },
35629
35630     animCollapse : function(callback){
35631         var ct = Roo.get(this.ctNode);
35632         ct.enableDisplayMode('block');
35633         ct.stopFx();
35634
35635         this.animating = true;
35636         this.updateExpandIcon();
35637
35638         ct.slideOut('t', {
35639             callback : function(){
35640                this.animating = false;
35641                Roo.callback(callback);
35642             },
35643             scope: this,
35644             duration: this.node.ownerTree.duration || .25
35645         });
35646     },
35647
35648     getContainer : function(){
35649         return this.ctNode;
35650     },
35651
35652     getEl : function(){
35653         return this.wrap;
35654     },
35655
35656     appendDDGhost : function(ghostNode){
35657         ghostNode.appendChild(this.elNode.cloneNode(true));
35658     },
35659
35660     getDDRepairXY : function(){
35661         return Roo.lib.Dom.getXY(this.iconNode);
35662     },
35663
35664     onRender : function(){
35665         this.render();
35666     },
35667
35668     render : function(bulkRender){
35669         var n = this.node, a = n.attributes;
35670         var targetNode = n.parentNode ?
35671               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35672
35673         if(!this.rendered){
35674             this.rendered = true;
35675
35676             this.renderElements(n, a, targetNode, bulkRender);
35677
35678             if(a.qtip){
35679                if(this.textNode.setAttributeNS){
35680                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35681                    if(a.qtipTitle){
35682                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35683                    }
35684                }else{
35685                    this.textNode.setAttribute("ext:qtip", a.qtip);
35686                    if(a.qtipTitle){
35687                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35688                    }
35689                }
35690             }else if(a.qtipCfg){
35691                 a.qtipCfg.target = Roo.id(this.textNode);
35692                 Roo.QuickTips.register(a.qtipCfg);
35693             }
35694             this.initEvents();
35695             if(!this.node.expanded){
35696                 this.updateExpandIcon();
35697             }
35698         }else{
35699             if(bulkRender === true) {
35700                 targetNode.appendChild(this.wrap);
35701             }
35702         }
35703     },
35704
35705     renderElements : function(n, a, targetNode, bulkRender)
35706     {
35707         // add some indent caching, this helps performance when rendering a large tree
35708         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35709         var t = n.getOwnerTree();
35710         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35711         if (typeof(n.attributes.html) != 'undefined') {
35712             txt = n.attributes.html;
35713         }
35714         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
35715         var cb = typeof a.checked == 'boolean';
35716         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35717         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35718             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35719             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35720             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35721             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35722             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35723              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35724                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35725             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35726             "</li>"];
35727
35728         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35729             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35730                                 n.nextSibling.ui.getEl(), buf.join(""));
35731         }else{
35732             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35733         }
35734
35735         this.elNode = this.wrap.childNodes[0];
35736         this.ctNode = this.wrap.childNodes[1];
35737         var cs = this.elNode.childNodes;
35738         this.indentNode = cs[0];
35739         this.ecNode = cs[1];
35740         this.iconNode = cs[2];
35741         var index = 3;
35742         if(cb){
35743             this.checkbox = cs[3];
35744             index++;
35745         }
35746         this.anchor = cs[index];
35747         this.textNode = cs[index].firstChild;
35748     },
35749
35750     getAnchor : function(){
35751         return this.anchor;
35752     },
35753
35754     getTextEl : function(){
35755         return this.textNode;
35756     },
35757
35758     getIconEl : function(){
35759         return this.iconNode;
35760     },
35761
35762     isChecked : function(){
35763         return this.checkbox ? this.checkbox.checked : false;
35764     },
35765
35766     updateExpandIcon : function(){
35767         if(this.rendered){
35768             var n = this.node, c1, c2;
35769             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
35770             var hasChild = n.hasChildNodes();
35771             if(hasChild){
35772                 if(n.expanded){
35773                     cls += "-minus";
35774                     c1 = "x-tree-node-collapsed";
35775                     c2 = "x-tree-node-expanded";
35776                 }else{
35777                     cls += "-plus";
35778                     c1 = "x-tree-node-expanded";
35779                     c2 = "x-tree-node-collapsed";
35780                 }
35781                 if(this.wasLeaf){
35782                     this.removeClass("x-tree-node-leaf");
35783                     this.wasLeaf = false;
35784                 }
35785                 if(this.c1 != c1 || this.c2 != c2){
35786                     Roo.fly(this.elNode).replaceClass(c1, c2);
35787                     this.c1 = c1; this.c2 = c2;
35788                 }
35789             }else{
35790                 // this changes non-leafs into leafs if they have no children.
35791                 // it's not very rational behaviour..
35792                 
35793                 if(!this.wasLeaf && this.node.leaf){
35794                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
35795                     delete this.c1;
35796                     delete this.c2;
35797                     this.wasLeaf = true;
35798                 }
35799             }
35800             var ecc = "x-tree-ec-icon "+cls;
35801             if(this.ecc != ecc){
35802                 this.ecNode.className = ecc;
35803                 this.ecc = ecc;
35804             }
35805         }
35806     },
35807
35808     getChildIndent : function(){
35809         if(!this.childIndent){
35810             var buf = [];
35811             var p = this.node;
35812             while(p){
35813                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
35814                     if(!p.isLast()) {
35815                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
35816                     } else {
35817                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
35818                     }
35819                 }
35820                 p = p.parentNode;
35821             }
35822             this.childIndent = buf.join("");
35823         }
35824         return this.childIndent;
35825     },
35826
35827     renderIndent : function(){
35828         if(this.rendered){
35829             var indent = "";
35830             var p = this.node.parentNode;
35831             if(p){
35832                 indent = p.ui.getChildIndent();
35833             }
35834             if(this.indentMarkup != indent){ // don't rerender if not required
35835                 this.indentNode.innerHTML = indent;
35836                 this.indentMarkup = indent;
35837             }
35838             this.updateExpandIcon();
35839         }
35840     }
35841 };
35842
35843 Roo.tree.RootTreeNodeUI = function(){
35844     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
35845 };
35846 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
35847     render : function(){
35848         if(!this.rendered){
35849             var targetNode = this.node.ownerTree.innerCt.dom;
35850             this.node.expanded = true;
35851             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
35852             this.wrap = this.ctNode = targetNode.firstChild;
35853         }
35854     },
35855     collapse : function(){
35856     },
35857     expand : function(){
35858     }
35859 });/*
35860  * Based on:
35861  * Ext JS Library 1.1.1
35862  * Copyright(c) 2006-2007, Ext JS, LLC.
35863  *
35864  * Originally Released Under LGPL - original licence link has changed is not relivant.
35865  *
35866  * Fork - LGPL
35867  * <script type="text/javascript">
35868  */
35869 /**
35870  * @class Roo.tree.TreeLoader
35871  * @extends Roo.util.Observable
35872  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
35873  * nodes from a specified URL. The response must be a javascript Array definition
35874  * who's elements are node definition objects. eg:
35875  * <pre><code>
35876 {  success : true,
35877    data :      [
35878    
35879     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
35880     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
35881     ]
35882 }
35883
35884
35885 </code></pre>
35886  * <br><br>
35887  * The old style respose with just an array is still supported, but not recommended.
35888  * <br><br>
35889  *
35890  * A server request is sent, and child nodes are loaded only when a node is expanded.
35891  * The loading node's id is passed to the server under the parameter name "node" to
35892  * enable the server to produce the correct child nodes.
35893  * <br><br>
35894  * To pass extra parameters, an event handler may be attached to the "beforeload"
35895  * event, and the parameters specified in the TreeLoader's baseParams property:
35896  * <pre><code>
35897     myTreeLoader.on("beforeload", function(treeLoader, node) {
35898         this.baseParams.category = node.attributes.category;
35899     }, this);
35900 </code></pre><
35901  * This would pass an HTTP parameter called "category" to the server containing
35902  * the value of the Node's "category" attribute.
35903  * @constructor
35904  * Creates a new Treeloader.
35905  * @param {Object} config A config object containing config properties.
35906  */
35907 Roo.tree.TreeLoader = function(config){
35908     this.baseParams = {};
35909     this.requestMethod = "POST";
35910     Roo.apply(this, config);
35911
35912     this.addEvents({
35913     
35914         /**
35915          * @event beforeload
35916          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
35917          * @param {Object} This TreeLoader object.
35918          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35919          * @param {Object} callback The callback function specified in the {@link #load} call.
35920          */
35921         beforeload : true,
35922         /**
35923          * @event load
35924          * Fires when the node has been successfuly loaded.
35925          * @param {Object} This TreeLoader object.
35926          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35927          * @param {Object} response The response object containing the data from the server.
35928          */
35929         load : true,
35930         /**
35931          * @event loadexception
35932          * Fires if the network request failed.
35933          * @param {Object} This TreeLoader object.
35934          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35935          * @param {Object} response The response object containing the data from the server.
35936          */
35937         loadexception : true,
35938         /**
35939          * @event create
35940          * Fires before a node is created, enabling you to return custom Node types 
35941          * @param {Object} This TreeLoader object.
35942          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
35943          */
35944         create : true
35945     });
35946
35947     Roo.tree.TreeLoader.superclass.constructor.call(this);
35948 };
35949
35950 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
35951     /**
35952     * @cfg {String} dataUrl The URL from which to request a Json string which
35953     * specifies an array of node definition object representing the child nodes
35954     * to be loaded.
35955     */
35956     /**
35957     * @cfg {String} requestMethod either GET or POST
35958     * defaults to POST (due to BC)
35959     * to be loaded.
35960     */
35961     /**
35962     * @cfg {Object} baseParams (optional) An object containing properties which
35963     * specify HTTP parameters to be passed to each request for child nodes.
35964     */
35965     /**
35966     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
35967     * created by this loader. If the attributes sent by the server have an attribute in this object,
35968     * they take priority.
35969     */
35970     /**
35971     * @cfg {Object} uiProviders (optional) An object containing properties which
35972     * 
35973     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
35974     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
35975     * <i>uiProvider</i> attribute of a returned child node is a string rather
35976     * than a reference to a TreeNodeUI implementation, this that string value
35977     * is used as a property name in the uiProviders object. You can define the provider named
35978     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
35979     */
35980     uiProviders : {},
35981
35982     /**
35983     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
35984     * child nodes before loading.
35985     */
35986     clearOnLoad : true,
35987
35988     /**
35989     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
35990     * property on loading, rather than expecting an array. (eg. more compatible to a standard
35991     * Grid query { data : [ .....] }
35992     */
35993     
35994     root : false,
35995      /**
35996     * @cfg {String} queryParam (optional) 
35997     * Name of the query as it will be passed on the querystring (defaults to 'node')
35998     * eg. the request will be ?node=[id]
35999     */
36000     
36001     
36002     queryParam: false,
36003     
36004     /**
36005      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36006      * This is called automatically when a node is expanded, but may be used to reload
36007      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36008      * @param {Roo.tree.TreeNode} node
36009      * @param {Function} callback
36010      */
36011     load : function(node, callback){
36012         if(this.clearOnLoad){
36013             while(node.firstChild){
36014                 node.removeChild(node.firstChild);
36015             }
36016         }
36017         if(node.attributes.children){ // preloaded json children
36018             var cs = node.attributes.children;
36019             for(var i = 0, len = cs.length; i < len; i++){
36020                 node.appendChild(this.createNode(cs[i]));
36021             }
36022             if(typeof callback == "function"){
36023                 callback();
36024             }
36025         }else if(this.dataUrl){
36026             this.requestData(node, callback);
36027         }
36028     },
36029
36030     getParams: function(node){
36031         var buf = [], bp = this.baseParams;
36032         for(var key in bp){
36033             if(typeof bp[key] != "function"){
36034                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36035             }
36036         }
36037         var n = this.queryParam === false ? 'node' : this.queryParam;
36038         buf.push(n + "=", encodeURIComponent(node.id));
36039         return buf.join("");
36040     },
36041
36042     requestData : function(node, callback){
36043         if(this.fireEvent("beforeload", this, node, callback) !== false){
36044             this.transId = Roo.Ajax.request({
36045                 method:this.requestMethod,
36046                 url: this.dataUrl||this.url,
36047                 success: this.handleResponse,
36048                 failure: this.handleFailure,
36049                 scope: this,
36050                 argument: {callback: callback, node: node},
36051                 params: this.getParams(node)
36052             });
36053         }else{
36054             // if the load is cancelled, make sure we notify
36055             // the node that we are done
36056             if(typeof callback == "function"){
36057                 callback();
36058             }
36059         }
36060     },
36061
36062     isLoading : function(){
36063         return this.transId ? true : false;
36064     },
36065
36066     abort : function(){
36067         if(this.isLoading()){
36068             Roo.Ajax.abort(this.transId);
36069         }
36070     },
36071
36072     // private
36073     createNode : function(attr)
36074     {
36075         // apply baseAttrs, nice idea Corey!
36076         if(this.baseAttrs){
36077             Roo.applyIf(attr, this.baseAttrs);
36078         }
36079         if(this.applyLoader !== false){
36080             attr.loader = this;
36081         }
36082         // uiProvider = depreciated..
36083         
36084         if(typeof(attr.uiProvider) == 'string'){
36085            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36086                 /**  eval:var:attr */ eval(attr.uiProvider);
36087         }
36088         if(typeof(this.uiProviders['default']) != 'undefined') {
36089             attr.uiProvider = this.uiProviders['default'];
36090         }
36091         
36092         this.fireEvent('create', this, attr);
36093         
36094         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36095         return(attr.leaf ?
36096                         new Roo.tree.TreeNode(attr) :
36097                         new Roo.tree.AsyncTreeNode(attr));
36098     },
36099
36100     processResponse : function(response, node, callback)
36101     {
36102         var json = response.responseText;
36103         try {
36104             
36105             var o = Roo.decode(json);
36106             
36107             if (this.root === false && typeof(o.success) != undefined) {
36108                 this.root = 'data'; // the default behaviour for list like data..
36109                 }
36110                 
36111             if (this.root !== false &&  !o.success) {
36112                 // it's a failure condition.
36113                 var a = response.argument;
36114                 this.fireEvent("loadexception", this, a.node, response);
36115                 Roo.log("Load failed - should have a handler really");
36116                 return;
36117             }
36118             
36119             
36120             
36121             if (this.root !== false) {
36122                  o = o[this.root];
36123             }
36124             
36125             for(var i = 0, len = o.length; i < len; i++){
36126                 var n = this.createNode(o[i]);
36127                 if(n){
36128                     node.appendChild(n);
36129                 }
36130             }
36131             if(typeof callback == "function"){
36132                 callback(this, node);
36133             }
36134         }catch(e){
36135             this.handleFailure(response);
36136         }
36137     },
36138
36139     handleResponse : function(response){
36140         this.transId = false;
36141         var a = response.argument;
36142         this.processResponse(response, a.node, a.callback);
36143         this.fireEvent("load", this, a.node, response);
36144     },
36145
36146     handleFailure : function(response)
36147     {
36148         // should handle failure better..
36149         this.transId = false;
36150         var a = response.argument;
36151         this.fireEvent("loadexception", this, a.node, response);
36152         if(typeof a.callback == "function"){
36153             a.callback(this, a.node);
36154         }
36155     }
36156 });/*
36157  * Based on:
36158  * Ext JS Library 1.1.1
36159  * Copyright(c) 2006-2007, Ext JS, LLC.
36160  *
36161  * Originally Released Under LGPL - original licence link has changed is not relivant.
36162  *
36163  * Fork - LGPL
36164  * <script type="text/javascript">
36165  */
36166
36167 /**
36168 * @class Roo.tree.TreeFilter
36169 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36170 * @param {TreePanel} tree
36171 * @param {Object} config (optional)
36172  */
36173 Roo.tree.TreeFilter = function(tree, config){
36174     this.tree = tree;
36175     this.filtered = {};
36176     Roo.apply(this, config);
36177 };
36178
36179 Roo.tree.TreeFilter.prototype = {
36180     clearBlank:false,
36181     reverse:false,
36182     autoClear:false,
36183     remove:false,
36184
36185      /**
36186      * Filter the data by a specific attribute.
36187      * @param {String/RegExp} value Either string that the attribute value
36188      * should start with or a RegExp to test against the attribute
36189      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36190      * @param {TreeNode} startNode (optional) The node to start the filter at.
36191      */
36192     filter : function(value, attr, startNode){
36193         attr = attr || "text";
36194         var f;
36195         if(typeof value == "string"){
36196             var vlen = value.length;
36197             // auto clear empty filter
36198             if(vlen == 0 && this.clearBlank){
36199                 this.clear();
36200                 return;
36201             }
36202             value = value.toLowerCase();
36203             f = function(n){
36204                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36205             };
36206         }else if(value.exec){ // regex?
36207             f = function(n){
36208                 return value.test(n.attributes[attr]);
36209             };
36210         }else{
36211             throw 'Illegal filter type, must be string or regex';
36212         }
36213         this.filterBy(f, null, startNode);
36214         },
36215
36216     /**
36217      * Filter by a function. The passed function will be called with each
36218      * node in the tree (or from the startNode). If the function returns true, the node is kept
36219      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36220      * @param {Function} fn The filter function
36221      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36222      */
36223     filterBy : function(fn, scope, startNode){
36224         startNode = startNode || this.tree.root;
36225         if(this.autoClear){
36226             this.clear();
36227         }
36228         var af = this.filtered, rv = this.reverse;
36229         var f = function(n){
36230             if(n == startNode){
36231                 return true;
36232             }
36233             if(af[n.id]){
36234                 return false;
36235             }
36236             var m = fn.call(scope || n, n);
36237             if(!m || rv){
36238                 af[n.id] = n;
36239                 n.ui.hide();
36240                 return false;
36241             }
36242             return true;
36243         };
36244         startNode.cascade(f);
36245         if(this.remove){
36246            for(var id in af){
36247                if(typeof id != "function"){
36248                    var n = af[id];
36249                    if(n && n.parentNode){
36250                        n.parentNode.removeChild(n);
36251                    }
36252                }
36253            }
36254         }
36255     },
36256
36257     /**
36258      * Clears the current filter. Note: with the "remove" option
36259      * set a filter cannot be cleared.
36260      */
36261     clear : function(){
36262         var t = this.tree;
36263         var af = this.filtered;
36264         for(var id in af){
36265             if(typeof id != "function"){
36266                 var n = af[id];
36267                 if(n){
36268                     n.ui.show();
36269                 }
36270             }
36271         }
36272         this.filtered = {};
36273     }
36274 };
36275 /*
36276  * Based on:
36277  * Ext JS Library 1.1.1
36278  * Copyright(c) 2006-2007, Ext JS, LLC.
36279  *
36280  * Originally Released Under LGPL - original licence link has changed is not relivant.
36281  *
36282  * Fork - LGPL
36283  * <script type="text/javascript">
36284  */
36285  
36286
36287 /**
36288  * @class Roo.tree.TreeSorter
36289  * Provides sorting of nodes in a TreePanel
36290  * 
36291  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36292  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36293  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36294  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36295  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36296  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36297  * @constructor
36298  * @param {TreePanel} tree
36299  * @param {Object} config
36300  */
36301 Roo.tree.TreeSorter = function(tree, config){
36302     Roo.apply(this, config);
36303     tree.on("beforechildrenrendered", this.doSort, this);
36304     tree.on("append", this.updateSort, this);
36305     tree.on("insert", this.updateSort, this);
36306     
36307     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36308     var p = this.property || "text";
36309     var sortType = this.sortType;
36310     var fs = this.folderSort;
36311     var cs = this.caseSensitive === true;
36312     var leafAttr = this.leafAttr || 'leaf';
36313
36314     this.sortFn = function(n1, n2){
36315         if(fs){
36316             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36317                 return 1;
36318             }
36319             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36320                 return -1;
36321             }
36322         }
36323         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36324         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36325         if(v1 < v2){
36326                         return dsc ? +1 : -1;
36327                 }else if(v1 > v2){
36328                         return dsc ? -1 : +1;
36329         }else{
36330                 return 0;
36331         }
36332     };
36333 };
36334
36335 Roo.tree.TreeSorter.prototype = {
36336     doSort : function(node){
36337         node.sort(this.sortFn);
36338     },
36339     
36340     compareNodes : function(n1, n2){
36341         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36342     },
36343     
36344     updateSort : function(tree, node){
36345         if(node.childrenRendered){
36346             this.doSort.defer(1, this, [node]);
36347         }
36348     }
36349 };/*
36350  * Based on:
36351  * Ext JS Library 1.1.1
36352  * Copyright(c) 2006-2007, Ext JS, LLC.
36353  *
36354  * Originally Released Under LGPL - original licence link has changed is not relivant.
36355  *
36356  * Fork - LGPL
36357  * <script type="text/javascript">
36358  */
36359
36360 if(Roo.dd.DropZone){
36361     
36362 Roo.tree.TreeDropZone = function(tree, config){
36363     this.allowParentInsert = false;
36364     this.allowContainerDrop = false;
36365     this.appendOnly = false;
36366     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36367     this.tree = tree;
36368     this.lastInsertClass = "x-tree-no-status";
36369     this.dragOverData = {};
36370 };
36371
36372 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36373     ddGroup : "TreeDD",
36374     scroll:  true,
36375     
36376     expandDelay : 1000,
36377     
36378     expandNode : function(node){
36379         if(node.hasChildNodes() && !node.isExpanded()){
36380             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36381         }
36382     },
36383     
36384     queueExpand : function(node){
36385         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36386     },
36387     
36388     cancelExpand : function(){
36389         if(this.expandProcId){
36390             clearTimeout(this.expandProcId);
36391             this.expandProcId = false;
36392         }
36393     },
36394     
36395     isValidDropPoint : function(n, pt, dd, e, data){
36396         if(!n || !data){ return false; }
36397         var targetNode = n.node;
36398         var dropNode = data.node;
36399         // default drop rules
36400         if(!(targetNode && targetNode.isTarget && pt)){
36401             return false;
36402         }
36403         if(pt == "append" && targetNode.allowChildren === false){
36404             return false;
36405         }
36406         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36407             return false;
36408         }
36409         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36410             return false;
36411         }
36412         // reuse the object
36413         var overEvent = this.dragOverData;
36414         overEvent.tree = this.tree;
36415         overEvent.target = targetNode;
36416         overEvent.data = data;
36417         overEvent.point = pt;
36418         overEvent.source = dd;
36419         overEvent.rawEvent = e;
36420         overEvent.dropNode = dropNode;
36421         overEvent.cancel = false;  
36422         var result = this.tree.fireEvent("nodedragover", overEvent);
36423         return overEvent.cancel === false && result !== false;
36424     },
36425     
36426     getDropPoint : function(e, n, dd)
36427     {
36428         var tn = n.node;
36429         if(tn.isRoot){
36430             return tn.allowChildren !== false ? "append" : false; // always append for root
36431         }
36432         var dragEl = n.ddel;
36433         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36434         var y = Roo.lib.Event.getPageY(e);
36435         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36436         
36437         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36438         var noAppend = tn.allowChildren === false;
36439         if(this.appendOnly || tn.parentNode.allowChildren === false){
36440             return noAppend ? false : "append";
36441         }
36442         var noBelow = false;
36443         if(!this.allowParentInsert){
36444             noBelow = tn.hasChildNodes() && tn.isExpanded();
36445         }
36446         var q = (b - t) / (noAppend ? 2 : 3);
36447         if(y >= t && y < (t + q)){
36448             return "above";
36449         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36450             return "below";
36451         }else{
36452             return "append";
36453         }
36454     },
36455     
36456     onNodeEnter : function(n, dd, e, data)
36457     {
36458         this.cancelExpand();
36459     },
36460     
36461     onNodeOver : function(n, dd, e, data)
36462     {
36463        
36464         var pt = this.getDropPoint(e, n, dd);
36465         var node = n.node;
36466         
36467         // auto node expand check
36468         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36469             this.queueExpand(node);
36470         }else if(pt != "append"){
36471             this.cancelExpand();
36472         }
36473         
36474         // set the insert point style on the target node
36475         var returnCls = this.dropNotAllowed;
36476         if(this.isValidDropPoint(n, pt, dd, e, data)){
36477            if(pt){
36478                var el = n.ddel;
36479                var cls;
36480                if(pt == "above"){
36481                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36482                    cls = "x-tree-drag-insert-above";
36483                }else if(pt == "below"){
36484                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36485                    cls = "x-tree-drag-insert-below";
36486                }else{
36487                    returnCls = "x-tree-drop-ok-append";
36488                    cls = "x-tree-drag-append";
36489                }
36490                if(this.lastInsertClass != cls){
36491                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36492                    this.lastInsertClass = cls;
36493                }
36494            }
36495        }
36496        return returnCls;
36497     },
36498     
36499     onNodeOut : function(n, dd, e, data){
36500         
36501         this.cancelExpand();
36502         this.removeDropIndicators(n);
36503     },
36504     
36505     onNodeDrop : function(n, dd, e, data){
36506         var point = this.getDropPoint(e, n, dd);
36507         var targetNode = n.node;
36508         targetNode.ui.startDrop();
36509         if(!this.isValidDropPoint(n, point, dd, e, data)){
36510             targetNode.ui.endDrop();
36511             return false;
36512         }
36513         // first try to find the drop node
36514         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36515         var dropEvent = {
36516             tree : this.tree,
36517             target: targetNode,
36518             data: data,
36519             point: point,
36520             source: dd,
36521             rawEvent: e,
36522             dropNode: dropNode,
36523             cancel: !dropNode   
36524         };
36525         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36526         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36527             targetNode.ui.endDrop();
36528             return false;
36529         }
36530         // allow target changing
36531         targetNode = dropEvent.target;
36532         if(point == "append" && !targetNode.isExpanded()){
36533             targetNode.expand(false, null, function(){
36534                 this.completeDrop(dropEvent);
36535             }.createDelegate(this));
36536         }else{
36537             this.completeDrop(dropEvent);
36538         }
36539         return true;
36540     },
36541     
36542     completeDrop : function(de){
36543         var ns = de.dropNode, p = de.point, t = de.target;
36544         if(!(ns instanceof Array)){
36545             ns = [ns];
36546         }
36547         var n;
36548         for(var i = 0, len = ns.length; i < len; i++){
36549             n = ns[i];
36550             if(p == "above"){
36551                 t.parentNode.insertBefore(n, t);
36552             }else if(p == "below"){
36553                 t.parentNode.insertBefore(n, t.nextSibling);
36554             }else{
36555                 t.appendChild(n);
36556             }
36557         }
36558         n.ui.focus();
36559         if(this.tree.hlDrop){
36560             n.ui.highlight();
36561         }
36562         t.ui.endDrop();
36563         this.tree.fireEvent("nodedrop", de);
36564     },
36565     
36566     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36567         if(this.tree.hlDrop){
36568             dropNode.ui.focus();
36569             dropNode.ui.highlight();
36570         }
36571         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36572     },
36573     
36574     getTree : function(){
36575         return this.tree;
36576     },
36577     
36578     removeDropIndicators : function(n){
36579         if(n && n.ddel){
36580             var el = n.ddel;
36581             Roo.fly(el).removeClass([
36582                     "x-tree-drag-insert-above",
36583                     "x-tree-drag-insert-below",
36584                     "x-tree-drag-append"]);
36585             this.lastInsertClass = "_noclass";
36586         }
36587     },
36588     
36589     beforeDragDrop : function(target, e, id){
36590         this.cancelExpand();
36591         return true;
36592     },
36593     
36594     afterRepair : function(data){
36595         if(data && Roo.enableFx){
36596             data.node.ui.highlight();
36597         }
36598         this.hideProxy();
36599     } 
36600     
36601 });
36602
36603 }
36604 /*
36605  * Based on:
36606  * Ext JS Library 1.1.1
36607  * Copyright(c) 2006-2007, Ext JS, LLC.
36608  *
36609  * Originally Released Under LGPL - original licence link has changed is not relivant.
36610  *
36611  * Fork - LGPL
36612  * <script type="text/javascript">
36613  */
36614  
36615
36616 if(Roo.dd.DragZone){
36617 Roo.tree.TreeDragZone = function(tree, config){
36618     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36619     this.tree = tree;
36620 };
36621
36622 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36623     ddGroup : "TreeDD",
36624    
36625     onBeforeDrag : function(data, e){
36626         var n = data.node;
36627         return n && n.draggable && !n.disabled;
36628     },
36629      
36630     
36631     onInitDrag : function(e){
36632         var data = this.dragData;
36633         this.tree.getSelectionModel().select(data.node);
36634         this.proxy.update("");
36635         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36636         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36637     },
36638     
36639     getRepairXY : function(e, data){
36640         return data.node.ui.getDDRepairXY();
36641     },
36642     
36643     onEndDrag : function(data, e){
36644         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36645         
36646         
36647     },
36648     
36649     onValidDrop : function(dd, e, id){
36650         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36651         this.hideProxy();
36652     },
36653     
36654     beforeInvalidDrop : function(e, id){
36655         // this scrolls the original position back into view
36656         var sm = this.tree.getSelectionModel();
36657         sm.clearSelections();
36658         sm.select(this.dragData.node);
36659     }
36660 });
36661 }/*
36662  * Based on:
36663  * Ext JS Library 1.1.1
36664  * Copyright(c) 2006-2007, Ext JS, LLC.
36665  *
36666  * Originally Released Under LGPL - original licence link has changed is not relivant.
36667  *
36668  * Fork - LGPL
36669  * <script type="text/javascript">
36670  */
36671 /**
36672  * @class Roo.tree.TreeEditor
36673  * @extends Roo.Editor
36674  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36675  * as the editor field.
36676  * @constructor
36677  * @param {Object} config (used to be the tree panel.)
36678  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36679  * 
36680  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36681  * @cfg {Roo.form.TextField|Object} field The field configuration
36682  *
36683  * 
36684  */
36685 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36686     var tree = config;
36687     var field;
36688     if (oldconfig) { // old style..
36689         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36690     } else {
36691         // new style..
36692         tree = config.tree;
36693         config.field = config.field  || {};
36694         config.field.xtype = 'TextField';
36695         field = Roo.factory(config.field, Roo.form);
36696     }
36697     config = config || {};
36698     
36699     
36700     this.addEvents({
36701         /**
36702          * @event beforenodeedit
36703          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36704          * false from the handler of this event.
36705          * @param {Editor} this
36706          * @param {Roo.tree.Node} node 
36707          */
36708         "beforenodeedit" : true
36709     });
36710     
36711     //Roo.log(config);
36712     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36713
36714     this.tree = tree;
36715
36716     tree.on('beforeclick', this.beforeNodeClick, this);
36717     tree.getTreeEl().on('mousedown', this.hide, this);
36718     this.on('complete', this.updateNode, this);
36719     this.on('beforestartedit', this.fitToTree, this);
36720     this.on('startedit', this.bindScroll, this, {delay:10});
36721     this.on('specialkey', this.onSpecialKey, this);
36722 };
36723
36724 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36725     /**
36726      * @cfg {String} alignment
36727      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36728      */
36729     alignment: "l-l",
36730     // inherit
36731     autoSize: false,
36732     /**
36733      * @cfg {Boolean} hideEl
36734      * True to hide the bound element while the editor is displayed (defaults to false)
36735      */
36736     hideEl : false,
36737     /**
36738      * @cfg {String} cls
36739      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
36740      */
36741     cls: "x-small-editor x-tree-editor",
36742     /**
36743      * @cfg {Boolean} shim
36744      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
36745      */
36746     shim:false,
36747     // inherit
36748     shadow:"frame",
36749     /**
36750      * @cfg {Number} maxWidth
36751      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
36752      * the containing tree element's size, it will be automatically limited for you to the container width, taking
36753      * scroll and client offsets into account prior to each edit.
36754      */
36755     maxWidth: 250,
36756
36757     editDelay : 350,
36758
36759     // private
36760     fitToTree : function(ed, el){
36761         var td = this.tree.getTreeEl().dom, nd = el.dom;
36762         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
36763             td.scrollLeft = nd.offsetLeft;
36764         }
36765         var w = Math.min(
36766                 this.maxWidth,
36767                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
36768         this.setSize(w, '');
36769         
36770         return this.fireEvent('beforenodeedit', this, this.editNode);
36771         
36772     },
36773
36774     // private
36775     triggerEdit : function(node){
36776         this.completeEdit();
36777         this.editNode = node;
36778         this.startEdit(node.ui.textNode, node.text);
36779     },
36780
36781     // private
36782     bindScroll : function(){
36783         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
36784     },
36785
36786     // private
36787     beforeNodeClick : function(node, e){
36788         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
36789         this.lastClick = new Date();
36790         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
36791             e.stopEvent();
36792             this.triggerEdit(node);
36793             return false;
36794         }
36795         return true;
36796     },
36797
36798     // private
36799     updateNode : function(ed, value){
36800         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
36801         this.editNode.setText(value);
36802     },
36803
36804     // private
36805     onHide : function(){
36806         Roo.tree.TreeEditor.superclass.onHide.call(this);
36807         if(this.editNode){
36808             this.editNode.ui.focus();
36809         }
36810     },
36811
36812     // private
36813     onSpecialKey : function(field, e){
36814         var k = e.getKey();
36815         if(k == e.ESC){
36816             e.stopEvent();
36817             this.cancelEdit();
36818         }else if(k == e.ENTER && !e.hasModifier()){
36819             e.stopEvent();
36820             this.completeEdit();
36821         }
36822     }
36823 });//<Script type="text/javascript">
36824 /*
36825  * Based on:
36826  * Ext JS Library 1.1.1
36827  * Copyright(c) 2006-2007, Ext JS, LLC.
36828  *
36829  * Originally Released Under LGPL - original licence link has changed is not relivant.
36830  *
36831  * Fork - LGPL
36832  * <script type="text/javascript">
36833  */
36834  
36835 /**
36836  * Not documented??? - probably should be...
36837  */
36838
36839 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
36840     //focus: Roo.emptyFn, // prevent odd scrolling behavior
36841     
36842     renderElements : function(n, a, targetNode, bulkRender){
36843         //consel.log("renderElements?");
36844         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36845
36846         var t = n.getOwnerTree();
36847         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
36848         
36849         var cols = t.columns;
36850         var bw = t.borderWidth;
36851         var c = cols[0];
36852         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36853          var cb = typeof a.checked == "boolean";
36854         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36855         var colcls = 'x-t-' + tid + '-c0';
36856         var buf = [
36857             '<li class="x-tree-node">',
36858             
36859                 
36860                 '<div class="x-tree-node-el ', a.cls,'">',
36861                     // extran...
36862                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
36863                 
36864                 
36865                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
36866                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
36867                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
36868                            (a.icon ? ' x-tree-node-inline-icon' : ''),
36869                            (a.iconCls ? ' '+a.iconCls : ''),
36870                            '" unselectable="on" />',
36871                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
36872                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
36873                              
36874                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36875                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
36876                             '<span unselectable="on" qtip="' + tx + '">',
36877                              tx,
36878                              '</span></a>' ,
36879                     '</div>',
36880                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36881                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
36882                  ];
36883         for(var i = 1, len = cols.length; i < len; i++){
36884             c = cols[i];
36885             colcls = 'x-t-' + tid + '-c' +i;
36886             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36887             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
36888                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
36889                       "</div>");
36890          }
36891          
36892          buf.push(
36893             '</a>',
36894             '<div class="x-clear"></div></div>',
36895             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36896             "</li>");
36897         
36898         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36899             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36900                                 n.nextSibling.ui.getEl(), buf.join(""));
36901         }else{
36902             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36903         }
36904         var el = this.wrap.firstChild;
36905         this.elRow = el;
36906         this.elNode = el.firstChild;
36907         this.ranchor = el.childNodes[1];
36908         this.ctNode = this.wrap.childNodes[1];
36909         var cs = el.firstChild.childNodes;
36910         this.indentNode = cs[0];
36911         this.ecNode = cs[1];
36912         this.iconNode = cs[2];
36913         var index = 3;
36914         if(cb){
36915             this.checkbox = cs[3];
36916             index++;
36917         }
36918         this.anchor = cs[index];
36919         
36920         this.textNode = cs[index].firstChild;
36921         
36922         //el.on("click", this.onClick, this);
36923         //el.on("dblclick", this.onDblClick, this);
36924         
36925         
36926        // console.log(this);
36927     },
36928     initEvents : function(){
36929         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
36930         
36931             
36932         var a = this.ranchor;
36933
36934         var el = Roo.get(a);
36935
36936         if(Roo.isOpera){ // opera render bug ignores the CSS
36937             el.setStyle("text-decoration", "none");
36938         }
36939
36940         el.on("click", this.onClick, this);
36941         el.on("dblclick", this.onDblClick, this);
36942         el.on("contextmenu", this.onContextMenu, this);
36943         
36944     },
36945     
36946     /*onSelectedChange : function(state){
36947         if(state){
36948             this.focus();
36949             this.addClass("x-tree-selected");
36950         }else{
36951             //this.blur();
36952             this.removeClass("x-tree-selected");
36953         }
36954     },*/
36955     addClass : function(cls){
36956         if(this.elRow){
36957             Roo.fly(this.elRow).addClass(cls);
36958         }
36959         
36960     },
36961     
36962     
36963     removeClass : function(cls){
36964         if(this.elRow){
36965             Roo.fly(this.elRow).removeClass(cls);
36966         }
36967     }
36968
36969     
36970     
36971 });//<Script type="text/javascript">
36972
36973 /*
36974  * Based on:
36975  * Ext JS Library 1.1.1
36976  * Copyright(c) 2006-2007, Ext JS, LLC.
36977  *
36978  * Originally Released Under LGPL - original licence link has changed is not relivant.
36979  *
36980  * Fork - LGPL
36981  * <script type="text/javascript">
36982  */
36983  
36984
36985 /**
36986  * @class Roo.tree.ColumnTree
36987  * @extends Roo.data.TreePanel
36988  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
36989  * @cfg {int} borderWidth  compined right/left border allowance
36990  * @constructor
36991  * @param {String/HTMLElement/Element} el The container element
36992  * @param {Object} config
36993  */
36994 Roo.tree.ColumnTree =  function(el, config)
36995 {
36996    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
36997    this.addEvents({
36998         /**
36999         * @event resize
37000         * Fire this event on a container when it resizes
37001         * @param {int} w Width
37002         * @param {int} h Height
37003         */
37004        "resize" : true
37005     });
37006     this.on('resize', this.onResize, this);
37007 };
37008
37009 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37010     //lines:false,
37011     
37012     
37013     borderWidth: Roo.isBorderBox ? 0 : 2, 
37014     headEls : false,
37015     
37016     render : function(){
37017         // add the header.....
37018        
37019         Roo.tree.ColumnTree.superclass.render.apply(this);
37020         
37021         this.el.addClass('x-column-tree');
37022         
37023         this.headers = this.el.createChild(
37024             {cls:'x-tree-headers'},this.innerCt.dom);
37025    
37026         var cols = this.columns, c;
37027         var totalWidth = 0;
37028         this.headEls = [];
37029         var  len = cols.length;
37030         for(var i = 0; i < len; i++){
37031              c = cols[i];
37032              totalWidth += c.width;
37033             this.headEls.push(this.headers.createChild({
37034                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37035                  cn: {
37036                      cls:'x-tree-hd-text',
37037                      html: c.header
37038                  },
37039                  style:'width:'+(c.width-this.borderWidth)+'px;'
37040              }));
37041         }
37042         this.headers.createChild({cls:'x-clear'});
37043         // prevent floats from wrapping when clipped
37044         this.headers.setWidth(totalWidth);
37045         //this.innerCt.setWidth(totalWidth);
37046         this.innerCt.setStyle({ overflow: 'auto' });
37047         this.onResize(this.width, this.height);
37048              
37049         
37050     },
37051     onResize : function(w,h)
37052     {
37053         this.height = h;
37054         this.width = w;
37055         // resize cols..
37056         this.innerCt.setWidth(this.width);
37057         this.innerCt.setHeight(this.height-20);
37058         
37059         // headers...
37060         var cols = this.columns, c;
37061         var totalWidth = 0;
37062         var expEl = false;
37063         var len = cols.length;
37064         for(var i = 0; i < len; i++){
37065             c = cols[i];
37066             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37067                 // it's the expander..
37068                 expEl  = this.headEls[i];
37069                 continue;
37070             }
37071             totalWidth += c.width;
37072             
37073         }
37074         if (expEl) {
37075             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37076         }
37077         this.headers.setWidth(w-20);
37078
37079         
37080         
37081         
37082     }
37083 });
37084 /*
37085  * Based on:
37086  * Ext JS Library 1.1.1
37087  * Copyright(c) 2006-2007, Ext JS, LLC.
37088  *
37089  * Originally Released Under LGPL - original licence link has changed is not relivant.
37090  *
37091  * Fork - LGPL
37092  * <script type="text/javascript">
37093  */
37094  
37095 /**
37096  * @class Roo.menu.Menu
37097  * @extends Roo.util.Observable
37098  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37099  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37100  * @constructor
37101  * Creates a new Menu
37102  * @param {Object} config Configuration options
37103  */
37104 Roo.menu.Menu = function(config){
37105     Roo.apply(this, config);
37106     this.id = this.id || Roo.id();
37107     this.addEvents({
37108         /**
37109          * @event beforeshow
37110          * Fires before this menu is displayed
37111          * @param {Roo.menu.Menu} this
37112          */
37113         beforeshow : true,
37114         /**
37115          * @event beforehide
37116          * Fires before this menu is hidden
37117          * @param {Roo.menu.Menu} this
37118          */
37119         beforehide : true,
37120         /**
37121          * @event show
37122          * Fires after this menu is displayed
37123          * @param {Roo.menu.Menu} this
37124          */
37125         show : true,
37126         /**
37127          * @event hide
37128          * Fires after this menu is hidden
37129          * @param {Roo.menu.Menu} this
37130          */
37131         hide : true,
37132         /**
37133          * @event click
37134          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37135          * @param {Roo.menu.Menu} this
37136          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37137          * @param {Roo.EventObject} e
37138          */
37139         click : true,
37140         /**
37141          * @event mouseover
37142          * Fires when the mouse is hovering over this menu
37143          * @param {Roo.menu.Menu} this
37144          * @param {Roo.EventObject} e
37145          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37146          */
37147         mouseover : true,
37148         /**
37149          * @event mouseout
37150          * Fires when the mouse exits this menu
37151          * @param {Roo.menu.Menu} this
37152          * @param {Roo.EventObject} e
37153          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37154          */
37155         mouseout : true,
37156         /**
37157          * @event itemclick
37158          * Fires when a menu item contained in this menu is clicked
37159          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37160          * @param {Roo.EventObject} e
37161          */
37162         itemclick: true
37163     });
37164     if (this.registerMenu) {
37165         Roo.menu.MenuMgr.register(this);
37166     }
37167     
37168     var mis = this.items;
37169     this.items = new Roo.util.MixedCollection();
37170     if(mis){
37171         this.add.apply(this, mis);
37172     }
37173 };
37174
37175 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37176     /**
37177      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37178      */
37179     minWidth : 120,
37180     /**
37181      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37182      * for bottom-right shadow (defaults to "sides")
37183      */
37184     shadow : "sides",
37185     /**
37186      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37187      * this menu (defaults to "tl-tr?")
37188      */
37189     subMenuAlign : "tl-tr?",
37190     /**
37191      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37192      * relative to its element of origin (defaults to "tl-bl?")
37193      */
37194     defaultAlign : "tl-bl?",
37195     /**
37196      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37197      */
37198     allowOtherMenus : false,
37199     /**
37200      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37201      */
37202     registerMenu : true,
37203
37204     hidden:true,
37205
37206     // private
37207     render : function(){
37208         if(this.el){
37209             return;
37210         }
37211         var el = this.el = new Roo.Layer({
37212             cls: "x-menu",
37213             shadow:this.shadow,
37214             constrain: false,
37215             parentEl: this.parentEl || document.body,
37216             zindex:15000
37217         });
37218
37219         this.keyNav = new Roo.menu.MenuNav(this);
37220
37221         if(this.plain){
37222             el.addClass("x-menu-plain");
37223         }
37224         if(this.cls){
37225             el.addClass(this.cls);
37226         }
37227         // generic focus element
37228         this.focusEl = el.createChild({
37229             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37230         });
37231         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37232         //disabling touch- as it's causing issues ..
37233         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37234         ul.on('click'   , this.onClick, this);
37235         
37236         
37237         ul.on("mouseover", this.onMouseOver, this);
37238         ul.on("mouseout", this.onMouseOut, this);
37239         this.items.each(function(item){
37240             if (item.hidden) {
37241                 return;
37242             }
37243             
37244             var li = document.createElement("li");
37245             li.className = "x-menu-list-item";
37246             ul.dom.appendChild(li);
37247             item.render(li, this);
37248         }, this);
37249         this.ul = ul;
37250         this.autoWidth();
37251     },
37252
37253     // private
37254     autoWidth : function(){
37255         var el = this.el, ul = this.ul;
37256         if(!el){
37257             return;
37258         }
37259         var w = this.width;
37260         if(w){
37261             el.setWidth(w);
37262         }else if(Roo.isIE){
37263             el.setWidth(this.minWidth);
37264             var t = el.dom.offsetWidth; // force recalc
37265             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37266         }
37267     },
37268
37269     // private
37270     delayAutoWidth : function(){
37271         if(this.rendered){
37272             if(!this.awTask){
37273                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37274             }
37275             this.awTask.delay(20);
37276         }
37277     },
37278
37279     // private
37280     findTargetItem : function(e){
37281         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37282         if(t && t.menuItemId){
37283             return this.items.get(t.menuItemId);
37284         }
37285     },
37286
37287     // private
37288     onClick : function(e){
37289         Roo.log("menu.onClick");
37290         var t = this.findTargetItem(e);
37291         if(!t){
37292             return;
37293         }
37294         Roo.log(e);
37295         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37296             if(t == this.activeItem && t.shouldDeactivate(e)){
37297                 this.activeItem.deactivate();
37298                 delete this.activeItem;
37299                 return;
37300             }
37301             if(t.canActivate){
37302                 this.setActiveItem(t, true);
37303             }
37304             return;
37305             
37306             
37307         }
37308         
37309         t.onClick(e);
37310         this.fireEvent("click", this, t, e);
37311     },
37312
37313     // private
37314     setActiveItem : function(item, autoExpand){
37315         if(item != this.activeItem){
37316             if(this.activeItem){
37317                 this.activeItem.deactivate();
37318             }
37319             this.activeItem = item;
37320             item.activate(autoExpand);
37321         }else if(autoExpand){
37322             item.expandMenu();
37323         }
37324     },
37325
37326     // private
37327     tryActivate : function(start, step){
37328         var items = this.items;
37329         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37330             var item = items.get(i);
37331             if(!item.disabled && item.canActivate){
37332                 this.setActiveItem(item, false);
37333                 return item;
37334             }
37335         }
37336         return false;
37337     },
37338
37339     // private
37340     onMouseOver : function(e){
37341         var t;
37342         if(t = this.findTargetItem(e)){
37343             if(t.canActivate && !t.disabled){
37344                 this.setActiveItem(t, true);
37345             }
37346         }
37347         this.fireEvent("mouseover", this, e, t);
37348     },
37349
37350     // private
37351     onMouseOut : function(e){
37352         var t;
37353         if(t = this.findTargetItem(e)){
37354             if(t == this.activeItem && t.shouldDeactivate(e)){
37355                 this.activeItem.deactivate();
37356                 delete this.activeItem;
37357             }
37358         }
37359         this.fireEvent("mouseout", this, e, t);
37360     },
37361
37362     /**
37363      * Read-only.  Returns true if the menu is currently displayed, else false.
37364      * @type Boolean
37365      */
37366     isVisible : function(){
37367         return this.el && !this.hidden;
37368     },
37369
37370     /**
37371      * Displays this menu relative to another element
37372      * @param {String/HTMLElement/Roo.Element} element The element to align to
37373      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37374      * the element (defaults to this.defaultAlign)
37375      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37376      */
37377     show : function(el, pos, parentMenu){
37378         this.parentMenu = parentMenu;
37379         if(!this.el){
37380             this.render();
37381         }
37382         this.fireEvent("beforeshow", this);
37383         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37384     },
37385
37386     /**
37387      * Displays this menu at a specific xy position
37388      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37389      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37390      */
37391     showAt : function(xy, parentMenu, /* private: */_e){
37392         this.parentMenu = parentMenu;
37393         if(!this.el){
37394             this.render();
37395         }
37396         if(_e !== false){
37397             this.fireEvent("beforeshow", this);
37398             xy = this.el.adjustForConstraints(xy);
37399         }
37400         this.el.setXY(xy);
37401         this.el.show();
37402         this.hidden = false;
37403         this.focus();
37404         this.fireEvent("show", this);
37405     },
37406
37407     focus : function(){
37408         if(!this.hidden){
37409             this.doFocus.defer(50, this);
37410         }
37411     },
37412
37413     doFocus : function(){
37414         if(!this.hidden){
37415             this.focusEl.focus();
37416         }
37417     },
37418
37419     /**
37420      * Hides this menu and optionally all parent menus
37421      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37422      */
37423     hide : function(deep){
37424         if(this.el && this.isVisible()){
37425             this.fireEvent("beforehide", this);
37426             if(this.activeItem){
37427                 this.activeItem.deactivate();
37428                 this.activeItem = null;
37429             }
37430             this.el.hide();
37431             this.hidden = true;
37432             this.fireEvent("hide", this);
37433         }
37434         if(deep === true && this.parentMenu){
37435             this.parentMenu.hide(true);
37436         }
37437     },
37438
37439     /**
37440      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37441      * Any of the following are valid:
37442      * <ul>
37443      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37444      * <li>An HTMLElement object which will be converted to a menu item</li>
37445      * <li>A menu item config object that will be created as a new menu item</li>
37446      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37447      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37448      * </ul>
37449      * Usage:
37450      * <pre><code>
37451 // Create the menu
37452 var menu = new Roo.menu.Menu();
37453
37454 // Create a menu item to add by reference
37455 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37456
37457 // Add a bunch of items at once using different methods.
37458 // Only the last item added will be returned.
37459 var item = menu.add(
37460     menuItem,                // add existing item by ref
37461     'Dynamic Item',          // new TextItem
37462     '-',                     // new separator
37463     { text: 'Config Item' }  // new item by config
37464 );
37465 </code></pre>
37466      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37467      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37468      */
37469     add : function(){
37470         var a = arguments, l = a.length, item;
37471         for(var i = 0; i < l; i++){
37472             var el = a[i];
37473             if ((typeof(el) == "object") && el.xtype && el.xns) {
37474                 el = Roo.factory(el, Roo.menu);
37475             }
37476             
37477             if(el.render){ // some kind of Item
37478                 item = this.addItem(el);
37479             }else if(typeof el == "string"){ // string
37480                 if(el == "separator" || el == "-"){
37481                     item = this.addSeparator();
37482                 }else{
37483                     item = this.addText(el);
37484                 }
37485             }else if(el.tagName || el.el){ // element
37486                 item = this.addElement(el);
37487             }else if(typeof el == "object"){ // must be menu item config?
37488                 item = this.addMenuItem(el);
37489             }
37490         }
37491         return item;
37492     },
37493
37494     /**
37495      * Returns this menu's underlying {@link Roo.Element} object
37496      * @return {Roo.Element} The element
37497      */
37498     getEl : function(){
37499         if(!this.el){
37500             this.render();
37501         }
37502         return this.el;
37503     },
37504
37505     /**
37506      * Adds a separator bar to the menu
37507      * @return {Roo.menu.Item} The menu item that was added
37508      */
37509     addSeparator : function(){
37510         return this.addItem(new Roo.menu.Separator());
37511     },
37512
37513     /**
37514      * Adds an {@link Roo.Element} object to the menu
37515      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37516      * @return {Roo.menu.Item} The menu item that was added
37517      */
37518     addElement : function(el){
37519         return this.addItem(new Roo.menu.BaseItem(el));
37520     },
37521
37522     /**
37523      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37524      * @param {Roo.menu.Item} item The menu item to add
37525      * @return {Roo.menu.Item} The menu item that was added
37526      */
37527     addItem : function(item){
37528         this.items.add(item);
37529         if(this.ul){
37530             var li = document.createElement("li");
37531             li.className = "x-menu-list-item";
37532             this.ul.dom.appendChild(li);
37533             item.render(li, this);
37534             this.delayAutoWidth();
37535         }
37536         return item;
37537     },
37538
37539     /**
37540      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37541      * @param {Object} config A MenuItem config object
37542      * @return {Roo.menu.Item} The menu item that was added
37543      */
37544     addMenuItem : function(config){
37545         if(!(config instanceof Roo.menu.Item)){
37546             if(typeof config.checked == "boolean"){ // must be check menu item config?
37547                 config = new Roo.menu.CheckItem(config);
37548             }else{
37549                 config = new Roo.menu.Item(config);
37550             }
37551         }
37552         return this.addItem(config);
37553     },
37554
37555     /**
37556      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37557      * @param {String} text The text to display in the menu item
37558      * @return {Roo.menu.Item} The menu item that was added
37559      */
37560     addText : function(text){
37561         return this.addItem(new Roo.menu.TextItem({ text : text }));
37562     },
37563
37564     /**
37565      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37566      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37567      * @param {Roo.menu.Item} item The menu item to add
37568      * @return {Roo.menu.Item} The menu item that was added
37569      */
37570     insert : function(index, item){
37571         this.items.insert(index, item);
37572         if(this.ul){
37573             var li = document.createElement("li");
37574             li.className = "x-menu-list-item";
37575             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37576             item.render(li, this);
37577             this.delayAutoWidth();
37578         }
37579         return item;
37580     },
37581
37582     /**
37583      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37584      * @param {Roo.menu.Item} item The menu item to remove
37585      */
37586     remove : function(item){
37587         this.items.removeKey(item.id);
37588         item.destroy();
37589     },
37590
37591     /**
37592      * Removes and destroys all items in the menu
37593      */
37594     removeAll : function(){
37595         var f;
37596         while(f = this.items.first()){
37597             this.remove(f);
37598         }
37599     }
37600 });
37601
37602 // MenuNav is a private utility class used internally by the Menu
37603 Roo.menu.MenuNav = function(menu){
37604     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37605     this.scope = this.menu = menu;
37606 };
37607
37608 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37609     doRelay : function(e, h){
37610         var k = e.getKey();
37611         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37612             this.menu.tryActivate(0, 1);
37613             return false;
37614         }
37615         return h.call(this.scope || this, e, this.menu);
37616     },
37617
37618     up : function(e, m){
37619         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37620             m.tryActivate(m.items.length-1, -1);
37621         }
37622     },
37623
37624     down : function(e, m){
37625         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37626             m.tryActivate(0, 1);
37627         }
37628     },
37629
37630     right : function(e, m){
37631         if(m.activeItem){
37632             m.activeItem.expandMenu(true);
37633         }
37634     },
37635
37636     left : function(e, m){
37637         m.hide();
37638         if(m.parentMenu && m.parentMenu.activeItem){
37639             m.parentMenu.activeItem.activate();
37640         }
37641     },
37642
37643     enter : function(e, m){
37644         if(m.activeItem){
37645             e.stopPropagation();
37646             m.activeItem.onClick(e);
37647             m.fireEvent("click", this, m.activeItem);
37648             return true;
37649         }
37650     }
37651 });/*
37652  * Based on:
37653  * Ext JS Library 1.1.1
37654  * Copyright(c) 2006-2007, Ext JS, LLC.
37655  *
37656  * Originally Released Under LGPL - original licence link has changed is not relivant.
37657  *
37658  * Fork - LGPL
37659  * <script type="text/javascript">
37660  */
37661  
37662 /**
37663  * @class Roo.menu.MenuMgr
37664  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37665  * @singleton
37666  */
37667 Roo.menu.MenuMgr = function(){
37668    var menus, active, groups = {}, attached = false, lastShow = new Date();
37669
37670    // private - called when first menu is created
37671    function init(){
37672        menus = {};
37673        active = new Roo.util.MixedCollection();
37674        Roo.get(document).addKeyListener(27, function(){
37675            if(active.length > 0){
37676                hideAll();
37677            }
37678        });
37679    }
37680
37681    // private
37682    function hideAll(){
37683        if(active && active.length > 0){
37684            var c = active.clone();
37685            c.each(function(m){
37686                m.hide();
37687            });
37688        }
37689    }
37690
37691    // private
37692    function onHide(m){
37693        active.remove(m);
37694        if(active.length < 1){
37695            Roo.get(document).un("mousedown", onMouseDown);
37696            attached = false;
37697        }
37698    }
37699
37700    // private
37701    function onShow(m){
37702        var last = active.last();
37703        lastShow = new Date();
37704        active.add(m);
37705        if(!attached){
37706            Roo.get(document).on("mousedown", onMouseDown);
37707            attached = true;
37708        }
37709        if(m.parentMenu){
37710           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37711           m.parentMenu.activeChild = m;
37712        }else if(last && last.isVisible()){
37713           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37714        }
37715    }
37716
37717    // private
37718    function onBeforeHide(m){
37719        if(m.activeChild){
37720            m.activeChild.hide();
37721        }
37722        if(m.autoHideTimer){
37723            clearTimeout(m.autoHideTimer);
37724            delete m.autoHideTimer;
37725        }
37726    }
37727
37728    // private
37729    function onBeforeShow(m){
37730        var pm = m.parentMenu;
37731        if(!pm && !m.allowOtherMenus){
37732            hideAll();
37733        }else if(pm && pm.activeChild && active != m){
37734            pm.activeChild.hide();
37735        }
37736    }
37737
37738    // private
37739    function onMouseDown(e){
37740        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
37741            hideAll();
37742        }
37743    }
37744
37745    // private
37746    function onBeforeCheck(mi, state){
37747        if(state){
37748            var g = groups[mi.group];
37749            for(var i = 0, l = g.length; i < l; i++){
37750                if(g[i] != mi){
37751                    g[i].setChecked(false);
37752                }
37753            }
37754        }
37755    }
37756
37757    return {
37758
37759        /**
37760         * Hides all menus that are currently visible
37761         */
37762        hideAll : function(){
37763             hideAll();  
37764        },
37765
37766        // private
37767        register : function(menu){
37768            if(!menus){
37769                init();
37770            }
37771            menus[menu.id] = menu;
37772            menu.on("beforehide", onBeforeHide);
37773            menu.on("hide", onHide);
37774            menu.on("beforeshow", onBeforeShow);
37775            menu.on("show", onShow);
37776            var g = menu.group;
37777            if(g && menu.events["checkchange"]){
37778                if(!groups[g]){
37779                    groups[g] = [];
37780                }
37781                groups[g].push(menu);
37782                menu.on("checkchange", onCheck);
37783            }
37784        },
37785
37786         /**
37787          * Returns a {@link Roo.menu.Menu} object
37788          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
37789          * be used to generate and return a new Menu instance.
37790          */
37791        get : function(menu){
37792            if(typeof menu == "string"){ // menu id
37793                return menus[menu];
37794            }else if(menu.events){  // menu instance
37795                return menu;
37796            }else if(typeof menu.length == 'number'){ // array of menu items?
37797                return new Roo.menu.Menu({items:menu});
37798            }else{ // otherwise, must be a config
37799                return new Roo.menu.Menu(menu);
37800            }
37801        },
37802
37803        // private
37804        unregister : function(menu){
37805            delete menus[menu.id];
37806            menu.un("beforehide", onBeforeHide);
37807            menu.un("hide", onHide);
37808            menu.un("beforeshow", onBeforeShow);
37809            menu.un("show", onShow);
37810            var g = menu.group;
37811            if(g && menu.events["checkchange"]){
37812                groups[g].remove(menu);
37813                menu.un("checkchange", onCheck);
37814            }
37815        },
37816
37817        // private
37818        registerCheckable : function(menuItem){
37819            var g = menuItem.group;
37820            if(g){
37821                if(!groups[g]){
37822                    groups[g] = [];
37823                }
37824                groups[g].push(menuItem);
37825                menuItem.on("beforecheckchange", onBeforeCheck);
37826            }
37827        },
37828
37829        // private
37830        unregisterCheckable : function(menuItem){
37831            var g = menuItem.group;
37832            if(g){
37833                groups[g].remove(menuItem);
37834                menuItem.un("beforecheckchange", onBeforeCheck);
37835            }
37836        }
37837    };
37838 }();/*
37839  * Based on:
37840  * Ext JS Library 1.1.1
37841  * Copyright(c) 2006-2007, Ext JS, LLC.
37842  *
37843  * Originally Released Under LGPL - original licence link has changed is not relivant.
37844  *
37845  * Fork - LGPL
37846  * <script type="text/javascript">
37847  */
37848  
37849
37850 /**
37851  * @class Roo.menu.BaseItem
37852  * @extends Roo.Component
37853  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
37854  * management and base configuration options shared by all menu components.
37855  * @constructor
37856  * Creates a new BaseItem
37857  * @param {Object} config Configuration options
37858  */
37859 Roo.menu.BaseItem = function(config){
37860     Roo.menu.BaseItem.superclass.constructor.call(this, config);
37861
37862     this.addEvents({
37863         /**
37864          * @event click
37865          * Fires when this item is clicked
37866          * @param {Roo.menu.BaseItem} this
37867          * @param {Roo.EventObject} e
37868          */
37869         click: true,
37870         /**
37871          * @event activate
37872          * Fires when this item is activated
37873          * @param {Roo.menu.BaseItem} this
37874          */
37875         activate : true,
37876         /**
37877          * @event deactivate
37878          * Fires when this item is deactivated
37879          * @param {Roo.menu.BaseItem} this
37880          */
37881         deactivate : true
37882     });
37883
37884     if(this.handler){
37885         this.on("click", this.handler, this.scope, true);
37886     }
37887 };
37888
37889 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
37890     /**
37891      * @cfg {Function} handler
37892      * A function that will handle the click event of this menu item (defaults to undefined)
37893      */
37894     /**
37895      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
37896      */
37897     canActivate : false,
37898     
37899      /**
37900      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
37901      */
37902     hidden: false,
37903     
37904     /**
37905      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
37906      */
37907     activeClass : "x-menu-item-active",
37908     /**
37909      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
37910      */
37911     hideOnClick : true,
37912     /**
37913      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
37914      */
37915     hideDelay : 100,
37916
37917     // private
37918     ctype: "Roo.menu.BaseItem",
37919
37920     // private
37921     actionMode : "container",
37922
37923     // private
37924     render : function(container, parentMenu){
37925         this.parentMenu = parentMenu;
37926         Roo.menu.BaseItem.superclass.render.call(this, container);
37927         this.container.menuItemId = this.id;
37928     },
37929
37930     // private
37931     onRender : function(container, position){
37932         this.el = Roo.get(this.el);
37933         container.dom.appendChild(this.el.dom);
37934     },
37935
37936     // private
37937     onClick : function(e){
37938         if(!this.disabled && this.fireEvent("click", this, e) !== false
37939                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
37940             this.handleClick(e);
37941         }else{
37942             e.stopEvent();
37943         }
37944     },
37945
37946     // private
37947     activate : function(){
37948         if(this.disabled){
37949             return false;
37950         }
37951         var li = this.container;
37952         li.addClass(this.activeClass);
37953         this.region = li.getRegion().adjust(2, 2, -2, -2);
37954         this.fireEvent("activate", this);
37955         return true;
37956     },
37957
37958     // private
37959     deactivate : function(){
37960         this.container.removeClass(this.activeClass);
37961         this.fireEvent("deactivate", this);
37962     },
37963
37964     // private
37965     shouldDeactivate : function(e){
37966         return !this.region || !this.region.contains(e.getPoint());
37967     },
37968
37969     // private
37970     handleClick : function(e){
37971         if(this.hideOnClick){
37972             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
37973         }
37974     },
37975
37976     // private
37977     expandMenu : function(autoActivate){
37978         // do nothing
37979     },
37980
37981     // private
37982     hideMenu : function(){
37983         // do nothing
37984     }
37985 });/*
37986  * Based on:
37987  * Ext JS Library 1.1.1
37988  * Copyright(c) 2006-2007, Ext JS, LLC.
37989  *
37990  * Originally Released Under LGPL - original licence link has changed is not relivant.
37991  *
37992  * Fork - LGPL
37993  * <script type="text/javascript">
37994  */
37995  
37996 /**
37997  * @class Roo.menu.Adapter
37998  * @extends Roo.menu.BaseItem
37999  * 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.
38000  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38001  * @constructor
38002  * Creates a new Adapter
38003  * @param {Object} config Configuration options
38004  */
38005 Roo.menu.Adapter = function(component, config){
38006     Roo.menu.Adapter.superclass.constructor.call(this, config);
38007     this.component = component;
38008 };
38009 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38010     // private
38011     canActivate : true,
38012
38013     // private
38014     onRender : function(container, position){
38015         this.component.render(container);
38016         this.el = this.component.getEl();
38017     },
38018
38019     // private
38020     activate : function(){
38021         if(this.disabled){
38022             return false;
38023         }
38024         this.component.focus();
38025         this.fireEvent("activate", this);
38026         return true;
38027     },
38028
38029     // private
38030     deactivate : function(){
38031         this.fireEvent("deactivate", this);
38032     },
38033
38034     // private
38035     disable : function(){
38036         this.component.disable();
38037         Roo.menu.Adapter.superclass.disable.call(this);
38038     },
38039
38040     // private
38041     enable : function(){
38042         this.component.enable();
38043         Roo.menu.Adapter.superclass.enable.call(this);
38044     }
38045 });/*
38046  * Based on:
38047  * Ext JS Library 1.1.1
38048  * Copyright(c) 2006-2007, Ext JS, LLC.
38049  *
38050  * Originally Released Under LGPL - original licence link has changed is not relivant.
38051  *
38052  * Fork - LGPL
38053  * <script type="text/javascript">
38054  */
38055
38056 /**
38057  * @class Roo.menu.TextItem
38058  * @extends Roo.menu.BaseItem
38059  * Adds a static text string to a menu, usually used as either a heading or group separator.
38060  * Note: old style constructor with text is still supported.
38061  * 
38062  * @constructor
38063  * Creates a new TextItem
38064  * @param {Object} cfg Configuration
38065  */
38066 Roo.menu.TextItem = function(cfg){
38067     if (typeof(cfg) == 'string') {
38068         this.text = cfg;
38069     } else {
38070         Roo.apply(this,cfg);
38071     }
38072     
38073     Roo.menu.TextItem.superclass.constructor.call(this);
38074 };
38075
38076 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38077     /**
38078      * @cfg {Boolean} text Text to show on item.
38079      */
38080     text : '',
38081     
38082     /**
38083      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38084      */
38085     hideOnClick : false,
38086     /**
38087      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38088      */
38089     itemCls : "x-menu-text",
38090
38091     // private
38092     onRender : function(){
38093         var s = document.createElement("span");
38094         s.className = this.itemCls;
38095         s.innerHTML = this.text;
38096         this.el = s;
38097         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38098     }
38099 });/*
38100  * Based on:
38101  * Ext JS Library 1.1.1
38102  * Copyright(c) 2006-2007, Ext JS, LLC.
38103  *
38104  * Originally Released Under LGPL - original licence link has changed is not relivant.
38105  *
38106  * Fork - LGPL
38107  * <script type="text/javascript">
38108  */
38109
38110 /**
38111  * @class Roo.menu.Separator
38112  * @extends Roo.menu.BaseItem
38113  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38114  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38115  * @constructor
38116  * @param {Object} config Configuration options
38117  */
38118 Roo.menu.Separator = function(config){
38119     Roo.menu.Separator.superclass.constructor.call(this, config);
38120 };
38121
38122 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38123     /**
38124      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38125      */
38126     itemCls : "x-menu-sep",
38127     /**
38128      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38129      */
38130     hideOnClick : false,
38131
38132     // private
38133     onRender : function(li){
38134         var s = document.createElement("span");
38135         s.className = this.itemCls;
38136         s.innerHTML = "&#160;";
38137         this.el = s;
38138         li.addClass("x-menu-sep-li");
38139         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38140     }
38141 });/*
38142  * Based on:
38143  * Ext JS Library 1.1.1
38144  * Copyright(c) 2006-2007, Ext JS, LLC.
38145  *
38146  * Originally Released Under LGPL - original licence link has changed is not relivant.
38147  *
38148  * Fork - LGPL
38149  * <script type="text/javascript">
38150  */
38151 /**
38152  * @class Roo.menu.Item
38153  * @extends Roo.menu.BaseItem
38154  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38155  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38156  * activation and click handling.
38157  * @constructor
38158  * Creates a new Item
38159  * @param {Object} config Configuration options
38160  */
38161 Roo.menu.Item = function(config){
38162     Roo.menu.Item.superclass.constructor.call(this, config);
38163     if(this.menu){
38164         this.menu = Roo.menu.MenuMgr.get(this.menu);
38165     }
38166 };
38167 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38168     
38169     /**
38170      * @cfg {String} text
38171      * The text to show on the menu item.
38172      */
38173     text: '',
38174      /**
38175      * @cfg {String} HTML to render in menu
38176      * The text to show on the menu item (HTML version).
38177      */
38178     html: '',
38179     /**
38180      * @cfg {String} icon
38181      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38182      */
38183     icon: undefined,
38184     /**
38185      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38186      */
38187     itemCls : "x-menu-item",
38188     /**
38189      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38190      */
38191     canActivate : true,
38192     /**
38193      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38194      */
38195     showDelay: 200,
38196     // doc'd in BaseItem
38197     hideDelay: 200,
38198
38199     // private
38200     ctype: "Roo.menu.Item",
38201     
38202     // private
38203     onRender : function(container, position){
38204         var el = document.createElement("a");
38205         el.hideFocus = true;
38206         el.unselectable = "on";
38207         el.href = this.href || "#";
38208         if(this.hrefTarget){
38209             el.target = this.hrefTarget;
38210         }
38211         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38212         
38213         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38214         
38215         el.innerHTML = String.format(
38216                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38217                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38218         this.el = el;
38219         Roo.menu.Item.superclass.onRender.call(this, container, position);
38220     },
38221
38222     /**
38223      * Sets the text to display in this menu item
38224      * @param {String} text The text to display
38225      * @param {Boolean} isHTML true to indicate text is pure html.
38226      */
38227     setText : function(text, isHTML){
38228         if (isHTML) {
38229             this.html = text;
38230         } else {
38231             this.text = text;
38232             this.html = '';
38233         }
38234         if(this.rendered){
38235             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38236      
38237             this.el.update(String.format(
38238                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38239                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38240             this.parentMenu.autoWidth();
38241         }
38242     },
38243
38244     // private
38245     handleClick : function(e){
38246         if(!this.href){ // if no link defined, stop the event automatically
38247             e.stopEvent();
38248         }
38249         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38250     },
38251
38252     // private
38253     activate : function(autoExpand){
38254         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38255             this.focus();
38256             if(autoExpand){
38257                 this.expandMenu();
38258             }
38259         }
38260         return true;
38261     },
38262
38263     // private
38264     shouldDeactivate : function(e){
38265         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38266             if(this.menu && this.menu.isVisible()){
38267                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38268             }
38269             return true;
38270         }
38271         return false;
38272     },
38273
38274     // private
38275     deactivate : function(){
38276         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38277         this.hideMenu();
38278     },
38279
38280     // private
38281     expandMenu : function(autoActivate){
38282         if(!this.disabled && this.menu){
38283             clearTimeout(this.hideTimer);
38284             delete this.hideTimer;
38285             if(!this.menu.isVisible() && !this.showTimer){
38286                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38287             }else if (this.menu.isVisible() && autoActivate){
38288                 this.menu.tryActivate(0, 1);
38289             }
38290         }
38291     },
38292
38293     // private
38294     deferExpand : function(autoActivate){
38295         delete this.showTimer;
38296         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38297         if(autoActivate){
38298             this.menu.tryActivate(0, 1);
38299         }
38300     },
38301
38302     // private
38303     hideMenu : function(){
38304         clearTimeout(this.showTimer);
38305         delete this.showTimer;
38306         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38307             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38308         }
38309     },
38310
38311     // private
38312     deferHide : function(){
38313         delete this.hideTimer;
38314         this.menu.hide();
38315     }
38316 });/*
38317  * Based on:
38318  * Ext JS Library 1.1.1
38319  * Copyright(c) 2006-2007, Ext JS, LLC.
38320  *
38321  * Originally Released Under LGPL - original licence link has changed is not relivant.
38322  *
38323  * Fork - LGPL
38324  * <script type="text/javascript">
38325  */
38326  
38327 /**
38328  * @class Roo.menu.CheckItem
38329  * @extends Roo.menu.Item
38330  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38331  * @constructor
38332  * Creates a new CheckItem
38333  * @param {Object} config Configuration options
38334  */
38335 Roo.menu.CheckItem = function(config){
38336     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38337     this.addEvents({
38338         /**
38339          * @event beforecheckchange
38340          * Fires before the checked value is set, providing an opportunity to cancel if needed
38341          * @param {Roo.menu.CheckItem} this
38342          * @param {Boolean} checked The new checked value that will be set
38343          */
38344         "beforecheckchange" : true,
38345         /**
38346          * @event checkchange
38347          * Fires after the checked value has been set
38348          * @param {Roo.menu.CheckItem} this
38349          * @param {Boolean} checked The checked value that was set
38350          */
38351         "checkchange" : true
38352     });
38353     if(this.checkHandler){
38354         this.on('checkchange', this.checkHandler, this.scope);
38355     }
38356 };
38357 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38358     /**
38359      * @cfg {String} group
38360      * All check items with the same group name will automatically be grouped into a single-select
38361      * radio button group (defaults to '')
38362      */
38363     /**
38364      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38365      */
38366     itemCls : "x-menu-item x-menu-check-item",
38367     /**
38368      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38369      */
38370     groupClass : "x-menu-group-item",
38371
38372     /**
38373      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38374      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38375      * initialized with checked = true will be rendered as checked.
38376      */
38377     checked: false,
38378
38379     // private
38380     ctype: "Roo.menu.CheckItem",
38381
38382     // private
38383     onRender : function(c){
38384         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38385         if(this.group){
38386             this.el.addClass(this.groupClass);
38387         }
38388         Roo.menu.MenuMgr.registerCheckable(this);
38389         if(this.checked){
38390             this.checked = false;
38391             this.setChecked(true, true);
38392         }
38393     },
38394
38395     // private
38396     destroy : function(){
38397         if(this.rendered){
38398             Roo.menu.MenuMgr.unregisterCheckable(this);
38399         }
38400         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38401     },
38402
38403     /**
38404      * Set the checked state of this item
38405      * @param {Boolean} checked The new checked value
38406      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38407      */
38408     setChecked : function(state, suppressEvent){
38409         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38410             if(this.container){
38411                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38412             }
38413             this.checked = state;
38414             if(suppressEvent !== true){
38415                 this.fireEvent("checkchange", this, state);
38416             }
38417         }
38418     },
38419
38420     // private
38421     handleClick : function(e){
38422        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38423            this.setChecked(!this.checked);
38424        }
38425        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38426     }
38427 });/*
38428  * Based on:
38429  * Ext JS Library 1.1.1
38430  * Copyright(c) 2006-2007, Ext JS, LLC.
38431  *
38432  * Originally Released Under LGPL - original licence link has changed is not relivant.
38433  *
38434  * Fork - LGPL
38435  * <script type="text/javascript">
38436  */
38437  
38438 /**
38439  * @class Roo.menu.DateItem
38440  * @extends Roo.menu.Adapter
38441  * A menu item that wraps the {@link Roo.DatPicker} component.
38442  * @constructor
38443  * Creates a new DateItem
38444  * @param {Object} config Configuration options
38445  */
38446 Roo.menu.DateItem = function(config){
38447     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38448     /** The Roo.DatePicker object @type Roo.DatePicker */
38449     this.picker = this.component;
38450     this.addEvents({select: true});
38451     
38452     this.picker.on("render", function(picker){
38453         picker.getEl().swallowEvent("click");
38454         picker.container.addClass("x-menu-date-item");
38455     });
38456
38457     this.picker.on("select", this.onSelect, this);
38458 };
38459
38460 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38461     // private
38462     onSelect : function(picker, date){
38463         this.fireEvent("select", this, date, picker);
38464         Roo.menu.DateItem.superclass.handleClick.call(this);
38465     }
38466 });/*
38467  * Based on:
38468  * Ext JS Library 1.1.1
38469  * Copyright(c) 2006-2007, Ext JS, LLC.
38470  *
38471  * Originally Released Under LGPL - original licence link has changed is not relivant.
38472  *
38473  * Fork - LGPL
38474  * <script type="text/javascript">
38475  */
38476  
38477 /**
38478  * @class Roo.menu.ColorItem
38479  * @extends Roo.menu.Adapter
38480  * A menu item that wraps the {@link Roo.ColorPalette} component.
38481  * @constructor
38482  * Creates a new ColorItem
38483  * @param {Object} config Configuration options
38484  */
38485 Roo.menu.ColorItem = function(config){
38486     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38487     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38488     this.palette = this.component;
38489     this.relayEvents(this.palette, ["select"]);
38490     if(this.selectHandler){
38491         this.on('select', this.selectHandler, this.scope);
38492     }
38493 };
38494 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38495  * Based on:
38496  * Ext JS Library 1.1.1
38497  * Copyright(c) 2006-2007, Ext JS, LLC.
38498  *
38499  * Originally Released Under LGPL - original licence link has changed is not relivant.
38500  *
38501  * Fork - LGPL
38502  * <script type="text/javascript">
38503  */
38504  
38505
38506 /**
38507  * @class Roo.menu.DateMenu
38508  * @extends Roo.menu.Menu
38509  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38510  * @constructor
38511  * Creates a new DateMenu
38512  * @param {Object} config Configuration options
38513  */
38514 Roo.menu.DateMenu = function(config){
38515     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38516     this.plain = true;
38517     var di = new Roo.menu.DateItem(config);
38518     this.add(di);
38519     /**
38520      * The {@link Roo.DatePicker} instance for this DateMenu
38521      * @type DatePicker
38522      */
38523     this.picker = di.picker;
38524     /**
38525      * @event select
38526      * @param {DatePicker} picker
38527      * @param {Date} date
38528      */
38529     this.relayEvents(di, ["select"]);
38530     this.on('beforeshow', function(){
38531         if(this.picker){
38532             this.picker.hideMonthPicker(false);
38533         }
38534     }, this);
38535 };
38536 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38537     cls:'x-date-menu'
38538 });/*
38539  * Based on:
38540  * Ext JS Library 1.1.1
38541  * Copyright(c) 2006-2007, Ext JS, LLC.
38542  *
38543  * Originally Released Under LGPL - original licence link has changed is not relivant.
38544  *
38545  * Fork - LGPL
38546  * <script type="text/javascript">
38547  */
38548  
38549
38550 /**
38551  * @class Roo.menu.ColorMenu
38552  * @extends Roo.menu.Menu
38553  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38554  * @constructor
38555  * Creates a new ColorMenu
38556  * @param {Object} config Configuration options
38557  */
38558 Roo.menu.ColorMenu = function(config){
38559     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38560     this.plain = true;
38561     var ci = new Roo.menu.ColorItem(config);
38562     this.add(ci);
38563     /**
38564      * The {@link Roo.ColorPalette} instance for this ColorMenu
38565      * @type ColorPalette
38566      */
38567     this.palette = ci.palette;
38568     /**
38569      * @event select
38570      * @param {ColorPalette} palette
38571      * @param {String} color
38572      */
38573     this.relayEvents(ci, ["select"]);
38574 };
38575 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
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  * @class Roo.form.Field
38588  * @extends Roo.BoxComponent
38589  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38590  * @constructor
38591  * Creates a new Field
38592  * @param {Object} config Configuration options
38593  */
38594 Roo.form.Field = function(config){
38595     Roo.form.Field.superclass.constructor.call(this, config);
38596 };
38597
38598 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38599     /**
38600      * @cfg {String} fieldLabel Label to use when rendering a form.
38601      */
38602        /**
38603      * @cfg {String} qtip Mouse over tip
38604      */
38605      
38606     /**
38607      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38608      */
38609     invalidClass : "x-form-invalid",
38610     /**
38611      * @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")
38612      */
38613     invalidText : "The value in this field is invalid",
38614     /**
38615      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38616      */
38617     focusClass : "x-form-focus",
38618     /**
38619      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38620       automatic validation (defaults to "keyup").
38621      */
38622     validationEvent : "keyup",
38623     /**
38624      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38625      */
38626     validateOnBlur : true,
38627     /**
38628      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38629      */
38630     validationDelay : 250,
38631     /**
38632      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38633      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38634      */
38635     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38636     /**
38637      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38638      */
38639     fieldClass : "x-form-field",
38640     /**
38641      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38642      *<pre>
38643 Value         Description
38644 -----------   ----------------------------------------------------------------------
38645 qtip          Display a quick tip when the user hovers over the field
38646 title         Display a default browser title attribute popup
38647 under         Add a block div beneath the field containing the error text
38648 side          Add an error icon to the right of the field with a popup on hover
38649 [element id]  Add the error text directly to the innerHTML of the specified element
38650 </pre>
38651      */
38652     msgTarget : 'qtip',
38653     /**
38654      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38655      */
38656     msgFx : 'normal',
38657
38658     /**
38659      * @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.
38660      */
38661     readOnly : false,
38662
38663     /**
38664      * @cfg {Boolean} disabled True to disable the field (defaults to false).
38665      */
38666     disabled : false,
38667
38668     /**
38669      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
38670      */
38671     inputType : undefined,
38672     
38673     /**
38674      * @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).
38675          */
38676         tabIndex : undefined,
38677         
38678     // private
38679     isFormField : true,
38680
38681     // private
38682     hasFocus : false,
38683     /**
38684      * @property {Roo.Element} fieldEl
38685      * Element Containing the rendered Field (with label etc.)
38686      */
38687     /**
38688      * @cfg {Mixed} value A value to initialize this field with.
38689      */
38690     value : undefined,
38691
38692     /**
38693      * @cfg {String} name The field's HTML name attribute.
38694      */
38695     /**
38696      * @cfg {String} cls A CSS class to apply to the field's underlying element.
38697      */
38698     // private
38699     loadedValue : false,
38700      
38701      
38702         // private ??
38703         initComponent : function(){
38704         Roo.form.Field.superclass.initComponent.call(this);
38705         this.addEvents({
38706             /**
38707              * @event focus
38708              * Fires when this field receives input focus.
38709              * @param {Roo.form.Field} this
38710              */
38711             focus : true,
38712             /**
38713              * @event blur
38714              * Fires when this field loses input focus.
38715              * @param {Roo.form.Field} this
38716              */
38717             blur : true,
38718             /**
38719              * @event specialkey
38720              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
38721              * {@link Roo.EventObject#getKey} to determine which key was pressed.
38722              * @param {Roo.form.Field} this
38723              * @param {Roo.EventObject} e The event object
38724              */
38725             specialkey : true,
38726             /**
38727              * @event change
38728              * Fires just before the field blurs if the field value has changed.
38729              * @param {Roo.form.Field} this
38730              * @param {Mixed} newValue The new value
38731              * @param {Mixed} oldValue The original value
38732              */
38733             change : true,
38734             /**
38735              * @event invalid
38736              * Fires after the field has been marked as invalid.
38737              * @param {Roo.form.Field} this
38738              * @param {String} msg The validation message
38739              */
38740             invalid : true,
38741             /**
38742              * @event valid
38743              * Fires after the field has been validated with no errors.
38744              * @param {Roo.form.Field} this
38745              */
38746             valid : true,
38747              /**
38748              * @event keyup
38749              * Fires after the key up
38750              * @param {Roo.form.Field} this
38751              * @param {Roo.EventObject}  e The event Object
38752              */
38753             keyup : true
38754         });
38755     },
38756
38757     /**
38758      * Returns the name attribute of the field if available
38759      * @return {String} name The field name
38760      */
38761     getName: function(){
38762          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
38763     },
38764
38765     // private
38766     onRender : function(ct, position){
38767         Roo.form.Field.superclass.onRender.call(this, ct, position);
38768         if(!this.el){
38769             var cfg = this.getAutoCreate();
38770             if(!cfg.name){
38771                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38772             }
38773             if (!cfg.name.length) {
38774                 delete cfg.name;
38775             }
38776             if(this.inputType){
38777                 cfg.type = this.inputType;
38778             }
38779             this.el = ct.createChild(cfg, position);
38780         }
38781         var type = this.el.dom.type;
38782         if(type){
38783             if(type == 'password'){
38784                 type = 'text';
38785             }
38786             this.el.addClass('x-form-'+type);
38787         }
38788         if(this.readOnly){
38789             this.el.dom.readOnly = true;
38790         }
38791         if(this.tabIndex !== undefined){
38792             this.el.dom.setAttribute('tabIndex', this.tabIndex);
38793         }
38794
38795         this.el.addClass([this.fieldClass, this.cls]);
38796         this.initValue();
38797     },
38798
38799     /**
38800      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
38801      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
38802      * @return {Roo.form.Field} this
38803      */
38804     applyTo : function(target){
38805         this.allowDomMove = false;
38806         this.el = Roo.get(target);
38807         this.render(this.el.dom.parentNode);
38808         return this;
38809     },
38810
38811     // private
38812     initValue : function(){
38813         if(this.value !== undefined){
38814             this.setValue(this.value);
38815         }else if(this.el.dom.value.length > 0){
38816             this.setValue(this.el.dom.value);
38817         }
38818     },
38819
38820     /**
38821      * Returns true if this field has been changed since it was originally loaded and is not disabled.
38822      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
38823      */
38824     isDirty : function() {
38825         if(this.disabled) {
38826             return false;
38827         }
38828         return String(this.getValue()) !== String(this.originalValue);
38829     },
38830
38831     /**
38832      * stores the current value in loadedValue
38833      */
38834     resetHasChanged : function()
38835     {
38836         this.loadedValue = String(this.getValue());
38837     },
38838     /**
38839      * checks the current value against the 'loaded' value.
38840      * Note - will return false if 'resetHasChanged' has not been called first.
38841      */
38842     hasChanged : function()
38843     {
38844         if(this.disabled || this.readOnly) {
38845             return false;
38846         }
38847         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
38848     },
38849     
38850     
38851     
38852     // private
38853     afterRender : function(){
38854         Roo.form.Field.superclass.afterRender.call(this);
38855         this.initEvents();
38856     },
38857
38858     // private
38859     fireKey : function(e){
38860         //Roo.log('field ' + e.getKey());
38861         if(e.isNavKeyPress()){
38862             this.fireEvent("specialkey", this, e);
38863         }
38864     },
38865
38866     /**
38867      * Resets the current field value to the originally loaded value and clears any validation messages
38868      */
38869     reset : function(){
38870         this.setValue(this.resetValue);
38871         this.clearInvalid();
38872     },
38873
38874     // private
38875     initEvents : function(){
38876         // safari killled keypress - so keydown is now used..
38877         this.el.on("keydown" , this.fireKey,  this);
38878         this.el.on("focus", this.onFocus,  this);
38879         this.el.on("blur", this.onBlur,  this);
38880         this.el.relayEvent('keyup', this);
38881
38882         // reference to original value for reset
38883         this.originalValue = this.getValue();
38884         this.resetValue =  this.getValue();
38885     },
38886
38887     // private
38888     onFocus : function(){
38889         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38890             this.el.addClass(this.focusClass);
38891         }
38892         if(!this.hasFocus){
38893             this.hasFocus = true;
38894             this.startValue = this.getValue();
38895             this.fireEvent("focus", this);
38896         }
38897     },
38898
38899     beforeBlur : Roo.emptyFn,
38900
38901     // private
38902     onBlur : function(){
38903         this.beforeBlur();
38904         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38905             this.el.removeClass(this.focusClass);
38906         }
38907         this.hasFocus = false;
38908         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
38909             this.validate();
38910         }
38911         var v = this.getValue();
38912         if(String(v) !== String(this.startValue)){
38913             this.fireEvent('change', this, v, this.startValue);
38914         }
38915         this.fireEvent("blur", this);
38916     },
38917
38918     /**
38919      * Returns whether or not the field value is currently valid
38920      * @param {Boolean} preventMark True to disable marking the field invalid
38921      * @return {Boolean} True if the value is valid, else false
38922      */
38923     isValid : function(preventMark){
38924         if(this.disabled){
38925             return true;
38926         }
38927         var restore = this.preventMark;
38928         this.preventMark = preventMark === true;
38929         var v = this.validateValue(this.processValue(this.getRawValue()));
38930         this.preventMark = restore;
38931         return v;
38932     },
38933
38934     /**
38935      * Validates the field value
38936      * @return {Boolean} True if the value is valid, else false
38937      */
38938     validate : function(){
38939         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
38940             this.clearInvalid();
38941             return true;
38942         }
38943         return false;
38944     },
38945
38946     processValue : function(value){
38947         return value;
38948     },
38949
38950     // private
38951     // Subclasses should provide the validation implementation by overriding this
38952     validateValue : function(value){
38953         return true;
38954     },
38955
38956     /**
38957      * Mark this field as invalid
38958      * @param {String} msg The validation message
38959      */
38960     markInvalid : function(msg){
38961         if(!this.rendered || this.preventMark){ // not rendered
38962             return;
38963         }
38964         
38965         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
38966         
38967         obj.el.addClass(this.invalidClass);
38968         msg = msg || this.invalidText;
38969         switch(this.msgTarget){
38970             case 'qtip':
38971                 obj.el.dom.qtip = msg;
38972                 obj.el.dom.qclass = 'x-form-invalid-tip';
38973                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
38974                     Roo.QuickTips.enable();
38975                 }
38976                 break;
38977             case 'title':
38978                 this.el.dom.title = msg;
38979                 break;
38980             case 'under':
38981                 if(!this.errorEl){
38982                     var elp = this.el.findParent('.x-form-element', 5, true);
38983                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
38984                     this.errorEl.setWidth(elp.getWidth(true)-20);
38985                 }
38986                 this.errorEl.update(msg);
38987                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
38988                 break;
38989             case 'side':
38990                 if(!this.errorIcon){
38991                     var elp = this.el.findParent('.x-form-element', 5, true);
38992                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
38993                 }
38994                 this.alignErrorIcon();
38995                 this.errorIcon.dom.qtip = msg;
38996                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
38997                 this.errorIcon.show();
38998                 this.on('resize', this.alignErrorIcon, this);
38999                 break;
39000             default:
39001                 var t = Roo.getDom(this.msgTarget);
39002                 t.innerHTML = msg;
39003                 t.style.display = this.msgDisplay;
39004                 break;
39005         }
39006         this.fireEvent('invalid', this, msg);
39007     },
39008
39009     // private
39010     alignErrorIcon : function(){
39011         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39012     },
39013
39014     /**
39015      * Clear any invalid styles/messages for this field
39016      */
39017     clearInvalid : function(){
39018         if(!this.rendered || this.preventMark){ // not rendered
39019             return;
39020         }
39021         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39022         
39023         obj.el.removeClass(this.invalidClass);
39024         switch(this.msgTarget){
39025             case 'qtip':
39026                 obj.el.dom.qtip = '';
39027                 break;
39028             case 'title':
39029                 this.el.dom.title = '';
39030                 break;
39031             case 'under':
39032                 if(this.errorEl){
39033                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39034                 }
39035                 break;
39036             case 'side':
39037                 if(this.errorIcon){
39038                     this.errorIcon.dom.qtip = '';
39039                     this.errorIcon.hide();
39040                     this.un('resize', this.alignErrorIcon, this);
39041                 }
39042                 break;
39043             default:
39044                 var t = Roo.getDom(this.msgTarget);
39045                 t.innerHTML = '';
39046                 t.style.display = 'none';
39047                 break;
39048         }
39049         this.fireEvent('valid', this);
39050     },
39051
39052     /**
39053      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39054      * @return {Mixed} value The field value
39055      */
39056     getRawValue : function(){
39057         var v = this.el.getValue();
39058         
39059         return v;
39060     },
39061
39062     /**
39063      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39064      * @return {Mixed} value The field value
39065      */
39066     getValue : function(){
39067         var v = this.el.getValue();
39068          
39069         return v;
39070     },
39071
39072     /**
39073      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39074      * @param {Mixed} value The value to set
39075      */
39076     setRawValue : function(v){
39077         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39078     },
39079
39080     /**
39081      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39082      * @param {Mixed} value The value to set
39083      */
39084     setValue : function(v){
39085         this.value = v;
39086         if(this.rendered){
39087             this.el.dom.value = (v === null || v === undefined ? '' : v);
39088              this.validate();
39089         }
39090     },
39091
39092     adjustSize : function(w, h){
39093         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39094         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39095         return s;
39096     },
39097
39098     adjustWidth : function(tag, w){
39099         tag = tag.toLowerCase();
39100         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39101             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39102                 if(tag == 'input'){
39103                     return w + 2;
39104                 }
39105                 if(tag == 'textarea'){
39106                     return w-2;
39107                 }
39108             }else if(Roo.isOpera){
39109                 if(tag == 'input'){
39110                     return w + 2;
39111                 }
39112                 if(tag == 'textarea'){
39113                     return w-2;
39114                 }
39115             }
39116         }
39117         return w;
39118     }
39119 });
39120
39121
39122 // anything other than normal should be considered experimental
39123 Roo.form.Field.msgFx = {
39124     normal : {
39125         show: function(msgEl, f){
39126             msgEl.setDisplayed('block');
39127         },
39128
39129         hide : function(msgEl, f){
39130             msgEl.setDisplayed(false).update('');
39131         }
39132     },
39133
39134     slide : {
39135         show: function(msgEl, f){
39136             msgEl.slideIn('t', {stopFx:true});
39137         },
39138
39139         hide : function(msgEl, f){
39140             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39141         }
39142     },
39143
39144     slideRight : {
39145         show: function(msgEl, f){
39146             msgEl.fixDisplay();
39147             msgEl.alignTo(f.el, 'tl-tr');
39148             msgEl.slideIn('l', {stopFx:true});
39149         },
39150
39151         hide : function(msgEl, f){
39152             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39153         }
39154     }
39155 };/*
39156  * Based on:
39157  * Ext JS Library 1.1.1
39158  * Copyright(c) 2006-2007, Ext JS, LLC.
39159  *
39160  * Originally Released Under LGPL - original licence link has changed is not relivant.
39161  *
39162  * Fork - LGPL
39163  * <script type="text/javascript">
39164  */
39165  
39166
39167 /**
39168  * @class Roo.form.TextField
39169  * @extends Roo.form.Field
39170  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39171  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39172  * @constructor
39173  * Creates a new TextField
39174  * @param {Object} config Configuration options
39175  */
39176 Roo.form.TextField = function(config){
39177     Roo.form.TextField.superclass.constructor.call(this, config);
39178     this.addEvents({
39179         /**
39180          * @event autosize
39181          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39182          * according to the default logic, but this event provides a hook for the developer to apply additional
39183          * logic at runtime to resize the field if needed.
39184              * @param {Roo.form.Field} this This text field
39185              * @param {Number} width The new field width
39186              */
39187         autosize : true
39188     });
39189 };
39190
39191 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39192     /**
39193      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39194      */
39195     grow : false,
39196     /**
39197      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39198      */
39199     growMin : 30,
39200     /**
39201      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39202      */
39203     growMax : 800,
39204     /**
39205      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39206      */
39207     vtype : null,
39208     /**
39209      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39210      */
39211     maskRe : null,
39212     /**
39213      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39214      */
39215     disableKeyFilter : false,
39216     /**
39217      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39218      */
39219     allowBlank : true,
39220     /**
39221      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39222      */
39223     minLength : 0,
39224     /**
39225      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39226      */
39227     maxLength : Number.MAX_VALUE,
39228     /**
39229      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39230      */
39231     minLengthText : "The minimum length for this field is {0}",
39232     /**
39233      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39234      */
39235     maxLengthText : "The maximum length for this field is {0}",
39236     /**
39237      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39238      */
39239     selectOnFocus : false,
39240     /**
39241      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39242      */
39243     blankText : "This field is required",
39244     /**
39245      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39246      * If available, this function will be called only after the basic validators all return true, and will be passed the
39247      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39248      */
39249     validator : null,
39250     /**
39251      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39252      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39253      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39254      */
39255     regex : null,
39256     /**
39257      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39258      */
39259     regexText : "",
39260     /**
39261      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39262      */
39263     emptyText : null,
39264    
39265
39266     // private
39267     initEvents : function()
39268     {
39269         if (this.emptyText) {
39270             this.el.attr('placeholder', this.emptyText);
39271         }
39272         
39273         Roo.form.TextField.superclass.initEvents.call(this);
39274         if(this.validationEvent == 'keyup'){
39275             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39276             this.el.on('keyup', this.filterValidation, this);
39277         }
39278         else if(this.validationEvent !== false){
39279             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39280         }
39281         
39282         if(this.selectOnFocus){
39283             this.on("focus", this.preFocus, this);
39284             
39285         }
39286         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39287             this.el.on("keypress", this.filterKeys, this);
39288         }
39289         if(this.grow){
39290             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39291             this.el.on("click", this.autoSize,  this);
39292         }
39293         if(this.el.is('input[type=password]') && Roo.isSafari){
39294             this.el.on('keydown', this.SafariOnKeyDown, this);
39295         }
39296     },
39297
39298     processValue : function(value){
39299         if(this.stripCharsRe){
39300             var newValue = value.replace(this.stripCharsRe, '');
39301             if(newValue !== value){
39302                 this.setRawValue(newValue);
39303                 return newValue;
39304             }
39305         }
39306         return value;
39307     },
39308
39309     filterValidation : function(e){
39310         if(!e.isNavKeyPress()){
39311             this.validationTask.delay(this.validationDelay);
39312         }
39313     },
39314
39315     // private
39316     onKeyUp : function(e){
39317         if(!e.isNavKeyPress()){
39318             this.autoSize();
39319         }
39320     },
39321
39322     /**
39323      * Resets the current field value to the originally-loaded value and clears any validation messages.
39324      *  
39325      */
39326     reset : function(){
39327         Roo.form.TextField.superclass.reset.call(this);
39328        
39329     },
39330
39331     
39332     // private
39333     preFocus : function(){
39334         
39335         if(this.selectOnFocus){
39336             this.el.dom.select();
39337         }
39338     },
39339
39340     
39341     // private
39342     filterKeys : function(e){
39343         var k = e.getKey();
39344         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39345             return;
39346         }
39347         var c = e.getCharCode(), cc = String.fromCharCode(c);
39348         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39349             return;
39350         }
39351         if(!this.maskRe.test(cc)){
39352             e.stopEvent();
39353         }
39354     },
39355
39356     setValue : function(v){
39357         
39358         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39359         
39360         this.autoSize();
39361     },
39362
39363     /**
39364      * Validates a value according to the field's validation rules and marks the field as invalid
39365      * if the validation fails
39366      * @param {Mixed} value The value to validate
39367      * @return {Boolean} True if the value is valid, else false
39368      */
39369     validateValue : function(value){
39370         if(value.length < 1)  { // if it's blank
39371              if(this.allowBlank){
39372                 this.clearInvalid();
39373                 return true;
39374              }else{
39375                 this.markInvalid(this.blankText);
39376                 return false;
39377              }
39378         }
39379         if(value.length < this.minLength){
39380             this.markInvalid(String.format(this.minLengthText, this.minLength));
39381             return false;
39382         }
39383         if(value.length > this.maxLength){
39384             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39385             return false;
39386         }
39387         if(this.vtype){
39388             var vt = Roo.form.VTypes;
39389             if(!vt[this.vtype](value, this)){
39390                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39391                 return false;
39392             }
39393         }
39394         if(typeof this.validator == "function"){
39395             var msg = this.validator(value);
39396             if(msg !== true){
39397                 this.markInvalid(msg);
39398                 return false;
39399             }
39400         }
39401         if(this.regex && !this.regex.test(value)){
39402             this.markInvalid(this.regexText);
39403             return false;
39404         }
39405         return true;
39406     },
39407
39408     /**
39409      * Selects text in this field
39410      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39411      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39412      */
39413     selectText : function(start, end){
39414         var v = this.getRawValue();
39415         if(v.length > 0){
39416             start = start === undefined ? 0 : start;
39417             end = end === undefined ? v.length : end;
39418             var d = this.el.dom;
39419             if(d.setSelectionRange){
39420                 d.setSelectionRange(start, end);
39421             }else if(d.createTextRange){
39422                 var range = d.createTextRange();
39423                 range.moveStart("character", start);
39424                 range.moveEnd("character", v.length-end);
39425                 range.select();
39426             }
39427         }
39428     },
39429
39430     /**
39431      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39432      * This only takes effect if grow = true, and fires the autosize event.
39433      */
39434     autoSize : function(){
39435         if(!this.grow || !this.rendered){
39436             return;
39437         }
39438         if(!this.metrics){
39439             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39440         }
39441         var el = this.el;
39442         var v = el.dom.value;
39443         var d = document.createElement('div');
39444         d.appendChild(document.createTextNode(v));
39445         v = d.innerHTML;
39446         d = null;
39447         v += "&#160;";
39448         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39449         this.el.setWidth(w);
39450         this.fireEvent("autosize", this, w);
39451     },
39452     
39453     // private
39454     SafariOnKeyDown : function(event)
39455     {
39456         // this is a workaround for a password hang bug on chrome/ webkit.
39457         
39458         var isSelectAll = false;
39459         
39460         if(this.el.dom.selectionEnd > 0){
39461             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39462         }
39463         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39464             event.preventDefault();
39465             this.setValue('');
39466             return;
39467         }
39468         
39469         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39470             
39471             event.preventDefault();
39472             // this is very hacky as keydown always get's upper case.
39473             
39474             var cc = String.fromCharCode(event.getCharCode());
39475             
39476             
39477             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39478             
39479         }
39480         
39481         
39482     }
39483 });/*
39484  * Based on:
39485  * Ext JS Library 1.1.1
39486  * Copyright(c) 2006-2007, Ext JS, LLC.
39487  *
39488  * Originally Released Under LGPL - original licence link has changed is not relivant.
39489  *
39490  * Fork - LGPL
39491  * <script type="text/javascript">
39492  */
39493  
39494 /**
39495  * @class Roo.form.Hidden
39496  * @extends Roo.form.TextField
39497  * Simple Hidden element used on forms 
39498  * 
39499  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39500  * 
39501  * @constructor
39502  * Creates a new Hidden form element.
39503  * @param {Object} config Configuration options
39504  */
39505
39506
39507
39508 // easy hidden field...
39509 Roo.form.Hidden = function(config){
39510     Roo.form.Hidden.superclass.constructor.call(this, config);
39511 };
39512   
39513 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39514     fieldLabel:      '',
39515     inputType:      'hidden',
39516     width:          50,
39517     allowBlank:     true,
39518     labelSeparator: '',
39519     hidden:         true,
39520     itemCls :       'x-form-item-display-none'
39521
39522
39523 });
39524
39525
39526 /*
39527  * Based on:
39528  * Ext JS Library 1.1.1
39529  * Copyright(c) 2006-2007, Ext JS, LLC.
39530  *
39531  * Originally Released Under LGPL - original licence link has changed is not relivant.
39532  *
39533  * Fork - LGPL
39534  * <script type="text/javascript">
39535  */
39536  
39537 /**
39538  * @class Roo.form.TriggerField
39539  * @extends Roo.form.TextField
39540  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39541  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39542  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39543  * for which you can provide a custom implementation.  For example:
39544  * <pre><code>
39545 var trigger = new Roo.form.TriggerField();
39546 trigger.onTriggerClick = myTriggerFn;
39547 trigger.applyTo('my-field');
39548 </code></pre>
39549  *
39550  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39551  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39552  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39553  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39554  * @constructor
39555  * Create a new TriggerField.
39556  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39557  * to the base TextField)
39558  */
39559 Roo.form.TriggerField = function(config){
39560     this.mimicing = false;
39561     Roo.form.TriggerField.superclass.constructor.call(this, config);
39562 };
39563
39564 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39565     /**
39566      * @cfg {String} triggerClass A CSS class to apply to the trigger
39567      */
39568     /**
39569      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39570      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39571      */
39572     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39573     /**
39574      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39575      */
39576     hideTrigger:false,
39577
39578     /** @cfg {Boolean} grow @hide */
39579     /** @cfg {Number} growMin @hide */
39580     /** @cfg {Number} growMax @hide */
39581
39582     /**
39583      * @hide 
39584      * @method
39585      */
39586     autoSize: Roo.emptyFn,
39587     // private
39588     monitorTab : true,
39589     // private
39590     deferHeight : true,
39591
39592     
39593     actionMode : 'wrap',
39594     // private
39595     onResize : function(w, h){
39596         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39597         if(typeof w == 'number'){
39598             var x = w - this.trigger.getWidth();
39599             this.el.setWidth(this.adjustWidth('input', x));
39600             this.trigger.setStyle('left', x+'px');
39601         }
39602     },
39603
39604     // private
39605     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39606
39607     // private
39608     getResizeEl : function(){
39609         return this.wrap;
39610     },
39611
39612     // private
39613     getPositionEl : function(){
39614         return this.wrap;
39615     },
39616
39617     // private
39618     alignErrorIcon : function(){
39619         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39620     },
39621
39622     // private
39623     onRender : function(ct, position){
39624         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39625         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39626         this.trigger = this.wrap.createChild(this.triggerConfig ||
39627                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39628         if(this.hideTrigger){
39629             this.trigger.setDisplayed(false);
39630         }
39631         this.initTrigger();
39632         if(!this.width){
39633             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39634         }
39635     },
39636
39637     // private
39638     initTrigger : function(){
39639         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39640         this.trigger.addClassOnOver('x-form-trigger-over');
39641         this.trigger.addClassOnClick('x-form-trigger-click');
39642     },
39643
39644     // private
39645     onDestroy : function(){
39646         if(this.trigger){
39647             this.trigger.removeAllListeners();
39648             this.trigger.remove();
39649         }
39650         if(this.wrap){
39651             this.wrap.remove();
39652         }
39653         Roo.form.TriggerField.superclass.onDestroy.call(this);
39654     },
39655
39656     // private
39657     onFocus : function(){
39658         Roo.form.TriggerField.superclass.onFocus.call(this);
39659         if(!this.mimicing){
39660             this.wrap.addClass('x-trigger-wrap-focus');
39661             this.mimicing = true;
39662             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
39663             if(this.monitorTab){
39664                 this.el.on("keydown", this.checkTab, this);
39665             }
39666         }
39667     },
39668
39669     // private
39670     checkTab : function(e){
39671         if(e.getKey() == e.TAB){
39672             this.triggerBlur();
39673         }
39674     },
39675
39676     // private
39677     onBlur : function(){
39678         // do nothing
39679     },
39680
39681     // private
39682     mimicBlur : function(e, t){
39683         if(!this.wrap.contains(t) && this.validateBlur()){
39684             this.triggerBlur();
39685         }
39686     },
39687
39688     // private
39689     triggerBlur : function(){
39690         this.mimicing = false;
39691         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
39692         if(this.monitorTab){
39693             this.el.un("keydown", this.checkTab, this);
39694         }
39695         this.wrap.removeClass('x-trigger-wrap-focus');
39696         Roo.form.TriggerField.superclass.onBlur.call(this);
39697     },
39698
39699     // private
39700     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
39701     validateBlur : function(e, t){
39702         return true;
39703     },
39704
39705     // private
39706     onDisable : function(){
39707         Roo.form.TriggerField.superclass.onDisable.call(this);
39708         if(this.wrap){
39709             this.wrap.addClass('x-item-disabled');
39710         }
39711     },
39712
39713     // private
39714     onEnable : function(){
39715         Roo.form.TriggerField.superclass.onEnable.call(this);
39716         if(this.wrap){
39717             this.wrap.removeClass('x-item-disabled');
39718         }
39719     },
39720
39721     // private
39722     onShow : function(){
39723         var ae = this.getActionEl();
39724         
39725         if(ae){
39726             ae.dom.style.display = '';
39727             ae.dom.style.visibility = 'visible';
39728         }
39729     },
39730
39731     // private
39732     
39733     onHide : function(){
39734         var ae = this.getActionEl();
39735         ae.dom.style.display = 'none';
39736     },
39737
39738     /**
39739      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
39740      * by an implementing function.
39741      * @method
39742      * @param {EventObject} e
39743      */
39744     onTriggerClick : Roo.emptyFn
39745 });
39746
39747 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
39748 // to be extended by an implementing class.  For an example of implementing this class, see the custom
39749 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
39750 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
39751     initComponent : function(){
39752         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
39753
39754         this.triggerConfig = {
39755             tag:'span', cls:'x-form-twin-triggers', cn:[
39756             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
39757             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
39758         ]};
39759     },
39760
39761     getTrigger : function(index){
39762         return this.triggers[index];
39763     },
39764
39765     initTrigger : function(){
39766         var ts = this.trigger.select('.x-form-trigger', true);
39767         this.wrap.setStyle('overflow', 'hidden');
39768         var triggerField = this;
39769         ts.each(function(t, all, index){
39770             t.hide = function(){
39771                 var w = triggerField.wrap.getWidth();
39772                 this.dom.style.display = 'none';
39773                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39774             };
39775             t.show = function(){
39776                 var w = triggerField.wrap.getWidth();
39777                 this.dom.style.display = '';
39778                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39779             };
39780             var triggerIndex = 'Trigger'+(index+1);
39781
39782             if(this['hide'+triggerIndex]){
39783                 t.dom.style.display = 'none';
39784             }
39785             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
39786             t.addClassOnOver('x-form-trigger-over');
39787             t.addClassOnClick('x-form-trigger-click');
39788         }, this);
39789         this.triggers = ts.elements;
39790     },
39791
39792     onTrigger1Click : Roo.emptyFn,
39793     onTrigger2Click : Roo.emptyFn
39794 });/*
39795  * Based on:
39796  * Ext JS Library 1.1.1
39797  * Copyright(c) 2006-2007, Ext JS, LLC.
39798  *
39799  * Originally Released Under LGPL - original licence link has changed is not relivant.
39800  *
39801  * Fork - LGPL
39802  * <script type="text/javascript">
39803  */
39804  
39805 /**
39806  * @class Roo.form.TextArea
39807  * @extends Roo.form.TextField
39808  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
39809  * support for auto-sizing.
39810  * @constructor
39811  * Creates a new TextArea
39812  * @param {Object} config Configuration options
39813  */
39814 Roo.form.TextArea = function(config){
39815     Roo.form.TextArea.superclass.constructor.call(this, config);
39816     // these are provided exchanges for backwards compat
39817     // minHeight/maxHeight were replaced by growMin/growMax to be
39818     // compatible with TextField growing config values
39819     if(this.minHeight !== undefined){
39820         this.growMin = this.minHeight;
39821     }
39822     if(this.maxHeight !== undefined){
39823         this.growMax = this.maxHeight;
39824     }
39825 };
39826
39827 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
39828     /**
39829      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
39830      */
39831     growMin : 60,
39832     /**
39833      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
39834      */
39835     growMax: 1000,
39836     /**
39837      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
39838      * in the field (equivalent to setting overflow: hidden, defaults to false)
39839      */
39840     preventScrollbars: false,
39841     /**
39842      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39843      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
39844      */
39845
39846     // private
39847     onRender : function(ct, position){
39848         if(!this.el){
39849             this.defaultAutoCreate = {
39850                 tag: "textarea",
39851                 style:"width:300px;height:60px;",
39852                 autocomplete: "new-password"
39853             };
39854         }
39855         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
39856         if(this.grow){
39857             this.textSizeEl = Roo.DomHelper.append(document.body, {
39858                 tag: "pre", cls: "x-form-grow-sizer"
39859             });
39860             if(this.preventScrollbars){
39861                 this.el.setStyle("overflow", "hidden");
39862             }
39863             this.el.setHeight(this.growMin);
39864         }
39865     },
39866
39867     onDestroy : function(){
39868         if(this.textSizeEl){
39869             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
39870         }
39871         Roo.form.TextArea.superclass.onDestroy.call(this);
39872     },
39873
39874     // private
39875     onKeyUp : function(e){
39876         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
39877             this.autoSize();
39878         }
39879     },
39880
39881     /**
39882      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
39883      * This only takes effect if grow = true, and fires the autosize event if the height changes.
39884      */
39885     autoSize : function(){
39886         if(!this.grow || !this.textSizeEl){
39887             return;
39888         }
39889         var el = this.el;
39890         var v = el.dom.value;
39891         var ts = this.textSizeEl;
39892
39893         ts.innerHTML = '';
39894         ts.appendChild(document.createTextNode(v));
39895         v = ts.innerHTML;
39896
39897         Roo.fly(ts).setWidth(this.el.getWidth());
39898         if(v.length < 1){
39899             v = "&#160;&#160;";
39900         }else{
39901             if(Roo.isIE){
39902                 v = v.replace(/\n/g, '<p>&#160;</p>');
39903             }
39904             v += "&#160;\n&#160;";
39905         }
39906         ts.innerHTML = v;
39907         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
39908         if(h != this.lastHeight){
39909             this.lastHeight = h;
39910             this.el.setHeight(h);
39911             this.fireEvent("autosize", this, h);
39912         }
39913     }
39914 });/*
39915  * Based on:
39916  * Ext JS Library 1.1.1
39917  * Copyright(c) 2006-2007, Ext JS, LLC.
39918  *
39919  * Originally Released Under LGPL - original licence link has changed is not relivant.
39920  *
39921  * Fork - LGPL
39922  * <script type="text/javascript">
39923  */
39924  
39925
39926 /**
39927  * @class Roo.form.NumberField
39928  * @extends Roo.form.TextField
39929  * Numeric text field that provides automatic keystroke filtering and numeric validation.
39930  * @constructor
39931  * Creates a new NumberField
39932  * @param {Object} config Configuration options
39933  */
39934 Roo.form.NumberField = function(config){
39935     Roo.form.NumberField.superclass.constructor.call(this, config);
39936 };
39937
39938 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
39939     /**
39940      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
39941      */
39942     fieldClass: "x-form-field x-form-num-field",
39943     /**
39944      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39945      */
39946     allowDecimals : true,
39947     /**
39948      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39949      */
39950     decimalSeparator : ".",
39951     /**
39952      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39953      */
39954     decimalPrecision : 2,
39955     /**
39956      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39957      */
39958     allowNegative : true,
39959     /**
39960      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39961      */
39962     minValue : Number.NEGATIVE_INFINITY,
39963     /**
39964      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39965      */
39966     maxValue : Number.MAX_VALUE,
39967     /**
39968      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39969      */
39970     minText : "The minimum value for this field is {0}",
39971     /**
39972      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39973      */
39974     maxText : "The maximum value for this field is {0}",
39975     /**
39976      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39977      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39978      */
39979     nanText : "{0} is not a valid number",
39980
39981     // private
39982     initEvents : function(){
39983         Roo.form.NumberField.superclass.initEvents.call(this);
39984         var allowed = "0123456789";
39985         if(this.allowDecimals){
39986             allowed += this.decimalSeparator;
39987         }
39988         if(this.allowNegative){
39989             allowed += "-";
39990         }
39991         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39992         var keyPress = function(e){
39993             var k = e.getKey();
39994             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39995                 return;
39996             }
39997             var c = e.getCharCode();
39998             if(allowed.indexOf(String.fromCharCode(c)) === -1){
39999                 e.stopEvent();
40000             }
40001         };
40002         this.el.on("keypress", keyPress, this);
40003     },
40004
40005     // private
40006     validateValue : function(value){
40007         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40008             return false;
40009         }
40010         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40011              return true;
40012         }
40013         var num = this.parseValue(value);
40014         if(isNaN(num)){
40015             this.markInvalid(String.format(this.nanText, value));
40016             return false;
40017         }
40018         if(num < this.minValue){
40019             this.markInvalid(String.format(this.minText, this.minValue));
40020             return false;
40021         }
40022         if(num > this.maxValue){
40023             this.markInvalid(String.format(this.maxText, this.maxValue));
40024             return false;
40025         }
40026         return true;
40027     },
40028
40029     getValue : function(){
40030         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40031     },
40032
40033     // private
40034     parseValue : function(value){
40035         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40036         return isNaN(value) ? '' : value;
40037     },
40038
40039     // private
40040     fixPrecision : function(value){
40041         var nan = isNaN(value);
40042         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40043             return nan ? '' : value;
40044         }
40045         return parseFloat(value).toFixed(this.decimalPrecision);
40046     },
40047
40048     setValue : function(v){
40049         v = this.fixPrecision(v);
40050         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40051     },
40052
40053     // private
40054     decimalPrecisionFcn : function(v){
40055         return Math.floor(v);
40056     },
40057
40058     beforeBlur : function(){
40059         var v = this.parseValue(this.getRawValue());
40060         if(v){
40061             this.setValue(v);
40062         }
40063     }
40064 });/*
40065  * Based on:
40066  * Ext JS Library 1.1.1
40067  * Copyright(c) 2006-2007, Ext JS, LLC.
40068  *
40069  * Originally Released Under LGPL - original licence link has changed is not relivant.
40070  *
40071  * Fork - LGPL
40072  * <script type="text/javascript">
40073  */
40074  
40075 /**
40076  * @class Roo.form.DateField
40077  * @extends Roo.form.TriggerField
40078  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40079 * @constructor
40080 * Create a new DateField
40081 * @param {Object} config
40082  */
40083 Roo.form.DateField = function(config){
40084     Roo.form.DateField.superclass.constructor.call(this, config);
40085     
40086       this.addEvents({
40087          
40088         /**
40089          * @event select
40090          * Fires when a date is selected
40091              * @param {Roo.form.DateField} combo This combo box
40092              * @param {Date} date The date selected
40093              */
40094         'select' : true
40095          
40096     });
40097     
40098     
40099     if(typeof this.minValue == "string") {
40100         this.minValue = this.parseDate(this.minValue);
40101     }
40102     if(typeof this.maxValue == "string") {
40103         this.maxValue = this.parseDate(this.maxValue);
40104     }
40105     this.ddMatch = null;
40106     if(this.disabledDates){
40107         var dd = this.disabledDates;
40108         var re = "(?:";
40109         for(var i = 0; i < dd.length; i++){
40110             re += dd[i];
40111             if(i != dd.length-1) {
40112                 re += "|";
40113             }
40114         }
40115         this.ddMatch = new RegExp(re + ")");
40116     }
40117 };
40118
40119 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40120     /**
40121      * @cfg {String} format
40122      * The default date format string which can be overriden for localization support.  The format must be
40123      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40124      */
40125     format : "m/d/y",
40126     /**
40127      * @cfg {String} altFormats
40128      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40129      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40130      */
40131     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40132     /**
40133      * @cfg {Array} disabledDays
40134      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40135      */
40136     disabledDays : null,
40137     /**
40138      * @cfg {String} disabledDaysText
40139      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40140      */
40141     disabledDaysText : "Disabled",
40142     /**
40143      * @cfg {Array} disabledDates
40144      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40145      * expression so they are very powerful. Some examples:
40146      * <ul>
40147      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40148      * <li>["03/08", "09/16"] would disable those days for every year</li>
40149      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40150      * <li>["03/../2006"] would disable every day in March 2006</li>
40151      * <li>["^03"] would disable every day in every March</li>
40152      * </ul>
40153      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40154      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40155      */
40156     disabledDates : null,
40157     /**
40158      * @cfg {String} disabledDatesText
40159      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40160      */
40161     disabledDatesText : "Disabled",
40162     /**
40163      * @cfg {Date/String} minValue
40164      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40165      * valid format (defaults to null).
40166      */
40167     minValue : null,
40168     /**
40169      * @cfg {Date/String} maxValue
40170      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40171      * valid format (defaults to null).
40172      */
40173     maxValue : null,
40174     /**
40175      * @cfg {String} minText
40176      * The error text to display when the date in the cell is before minValue (defaults to
40177      * 'The date in this field must be after {minValue}').
40178      */
40179     minText : "The date in this field must be equal to or after {0}",
40180     /**
40181      * @cfg {String} maxText
40182      * The error text to display when the date in the cell is after maxValue (defaults to
40183      * 'The date in this field must be before {maxValue}').
40184      */
40185     maxText : "The date in this field must be equal to or before {0}",
40186     /**
40187      * @cfg {String} invalidText
40188      * The error text to display when the date in the field is invalid (defaults to
40189      * '{value} is not a valid date - it must be in the format {format}').
40190      */
40191     invalidText : "{0} is not a valid date - it must be in the format {1}",
40192     /**
40193      * @cfg {String} triggerClass
40194      * An additional CSS class used to style the trigger button.  The trigger will always get the
40195      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40196      * which displays a calendar icon).
40197      */
40198     triggerClass : 'x-form-date-trigger',
40199     
40200
40201     /**
40202      * @cfg {Boolean} useIso
40203      * if enabled, then the date field will use a hidden field to store the 
40204      * real value as iso formated date. default (false)
40205      */ 
40206     useIso : false,
40207     /**
40208      * @cfg {String/Object} autoCreate
40209      * A DomHelper element spec, or true for a default element spec (defaults to
40210      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40211      */ 
40212     // private
40213     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40214     
40215     // private
40216     hiddenField: false,
40217     
40218     onRender : function(ct, position)
40219     {
40220         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40221         if (this.useIso) {
40222             //this.el.dom.removeAttribute('name'); 
40223             Roo.log("Changing name?");
40224             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40225             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40226                     'before', true);
40227             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40228             // prevent input submission
40229             this.hiddenName = this.name;
40230         }
40231             
40232             
40233     },
40234     
40235     // private
40236     validateValue : function(value)
40237     {
40238         value = this.formatDate(value);
40239         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40240             Roo.log('super failed');
40241             return false;
40242         }
40243         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40244              return true;
40245         }
40246         var svalue = value;
40247         value = this.parseDate(value);
40248         if(!value){
40249             Roo.log('parse date failed' + svalue);
40250             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40251             return false;
40252         }
40253         var time = value.getTime();
40254         if(this.minValue && time < this.minValue.getTime()){
40255             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40256             return false;
40257         }
40258         if(this.maxValue && time > this.maxValue.getTime()){
40259             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40260             return false;
40261         }
40262         if(this.disabledDays){
40263             var day = value.getDay();
40264             for(var i = 0; i < this.disabledDays.length; i++) {
40265                 if(day === this.disabledDays[i]){
40266                     this.markInvalid(this.disabledDaysText);
40267                     return false;
40268                 }
40269             }
40270         }
40271         var fvalue = this.formatDate(value);
40272         if(this.ddMatch && this.ddMatch.test(fvalue)){
40273             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40274             return false;
40275         }
40276         return true;
40277     },
40278
40279     // private
40280     // Provides logic to override the default TriggerField.validateBlur which just returns true
40281     validateBlur : function(){
40282         return !this.menu || !this.menu.isVisible();
40283     },
40284     
40285     getName: function()
40286     {
40287         // returns hidden if it's set..
40288         if (!this.rendered) {return ''};
40289         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40290         
40291     },
40292
40293     /**
40294      * Returns the current date value of the date field.
40295      * @return {Date} The date value
40296      */
40297     getValue : function(){
40298         
40299         return  this.hiddenField ?
40300                 this.hiddenField.value :
40301                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40302     },
40303
40304     /**
40305      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40306      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40307      * (the default format used is "m/d/y").
40308      * <br />Usage:
40309      * <pre><code>
40310 //All of these calls set the same date value (May 4, 2006)
40311
40312 //Pass a date object:
40313 var dt = new Date('5/4/06');
40314 dateField.setValue(dt);
40315
40316 //Pass a date string (default format):
40317 dateField.setValue('5/4/06');
40318
40319 //Pass a date string (custom format):
40320 dateField.format = 'Y-m-d';
40321 dateField.setValue('2006-5-4');
40322 </code></pre>
40323      * @param {String/Date} date The date or valid date string
40324      */
40325     setValue : function(date){
40326         if (this.hiddenField) {
40327             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40328         }
40329         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40330         // make sure the value field is always stored as a date..
40331         this.value = this.parseDate(date);
40332         
40333         
40334     },
40335
40336     // private
40337     parseDate : function(value){
40338         if(!value || value instanceof Date){
40339             return value;
40340         }
40341         var v = Date.parseDate(value, this.format);
40342          if (!v && this.useIso) {
40343             v = Date.parseDate(value, 'Y-m-d');
40344         }
40345         if(!v && this.altFormats){
40346             if(!this.altFormatsArray){
40347                 this.altFormatsArray = this.altFormats.split("|");
40348             }
40349             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40350                 v = Date.parseDate(value, this.altFormatsArray[i]);
40351             }
40352         }
40353         return v;
40354     },
40355
40356     // private
40357     formatDate : function(date, fmt){
40358         return (!date || !(date instanceof Date)) ?
40359                date : date.dateFormat(fmt || this.format);
40360     },
40361
40362     // private
40363     menuListeners : {
40364         select: function(m, d){
40365             
40366             this.setValue(d);
40367             this.fireEvent('select', this, d);
40368         },
40369         show : function(){ // retain focus styling
40370             this.onFocus();
40371         },
40372         hide : function(){
40373             this.focus.defer(10, this);
40374             var ml = this.menuListeners;
40375             this.menu.un("select", ml.select,  this);
40376             this.menu.un("show", ml.show,  this);
40377             this.menu.un("hide", ml.hide,  this);
40378         }
40379     },
40380
40381     // private
40382     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40383     onTriggerClick : function(){
40384         if(this.disabled){
40385             return;
40386         }
40387         if(this.menu == null){
40388             this.menu = new Roo.menu.DateMenu();
40389         }
40390         Roo.apply(this.menu.picker,  {
40391             showClear: this.allowBlank,
40392             minDate : this.minValue,
40393             maxDate : this.maxValue,
40394             disabledDatesRE : this.ddMatch,
40395             disabledDatesText : this.disabledDatesText,
40396             disabledDays : this.disabledDays,
40397             disabledDaysText : this.disabledDaysText,
40398             format : this.useIso ? 'Y-m-d' : this.format,
40399             minText : String.format(this.minText, this.formatDate(this.minValue)),
40400             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40401         });
40402         this.menu.on(Roo.apply({}, this.menuListeners, {
40403             scope:this
40404         }));
40405         this.menu.picker.setValue(this.getValue() || new Date());
40406         this.menu.show(this.el, "tl-bl?");
40407     },
40408
40409     beforeBlur : function(){
40410         var v = this.parseDate(this.getRawValue());
40411         if(v){
40412             this.setValue(v);
40413         }
40414     },
40415
40416     /*@
40417      * overide
40418      * 
40419      */
40420     isDirty : function() {
40421         if(this.disabled) {
40422             return false;
40423         }
40424         
40425         if(typeof(this.startValue) === 'undefined'){
40426             return false;
40427         }
40428         
40429         return String(this.getValue()) !== String(this.startValue);
40430         
40431     }
40432 });/*
40433  * Based on:
40434  * Ext JS Library 1.1.1
40435  * Copyright(c) 2006-2007, Ext JS, LLC.
40436  *
40437  * Originally Released Under LGPL - original licence link has changed is not relivant.
40438  *
40439  * Fork - LGPL
40440  * <script type="text/javascript">
40441  */
40442  
40443 /**
40444  * @class Roo.form.MonthField
40445  * @extends Roo.form.TriggerField
40446  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40447 * @constructor
40448 * Create a new MonthField
40449 * @param {Object} config
40450  */
40451 Roo.form.MonthField = function(config){
40452     
40453     Roo.form.MonthField.superclass.constructor.call(this, config);
40454     
40455       this.addEvents({
40456          
40457         /**
40458          * @event select
40459          * Fires when a date is selected
40460              * @param {Roo.form.MonthFieeld} combo This combo box
40461              * @param {Date} date The date selected
40462              */
40463         'select' : true
40464          
40465     });
40466     
40467     
40468     if(typeof this.minValue == "string") {
40469         this.minValue = this.parseDate(this.minValue);
40470     }
40471     if(typeof this.maxValue == "string") {
40472         this.maxValue = this.parseDate(this.maxValue);
40473     }
40474     this.ddMatch = null;
40475     if(this.disabledDates){
40476         var dd = this.disabledDates;
40477         var re = "(?:";
40478         for(var i = 0; i < dd.length; i++){
40479             re += dd[i];
40480             if(i != dd.length-1) {
40481                 re += "|";
40482             }
40483         }
40484         this.ddMatch = new RegExp(re + ")");
40485     }
40486 };
40487
40488 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40489     /**
40490      * @cfg {String} format
40491      * The default date format string which can be overriden for localization support.  The format must be
40492      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40493      */
40494     format : "M Y",
40495     /**
40496      * @cfg {String} altFormats
40497      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40498      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40499      */
40500     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40501     /**
40502      * @cfg {Array} disabledDays
40503      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40504      */
40505     disabledDays : [0,1,2,3,4,5,6],
40506     /**
40507      * @cfg {String} disabledDaysText
40508      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40509      */
40510     disabledDaysText : "Disabled",
40511     /**
40512      * @cfg {Array} disabledDates
40513      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40514      * expression so they are very powerful. Some examples:
40515      * <ul>
40516      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40517      * <li>["03/08", "09/16"] would disable those days for every year</li>
40518      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40519      * <li>["03/../2006"] would disable every day in March 2006</li>
40520      * <li>["^03"] would disable every day in every March</li>
40521      * </ul>
40522      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40523      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40524      */
40525     disabledDates : null,
40526     /**
40527      * @cfg {String} disabledDatesText
40528      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40529      */
40530     disabledDatesText : "Disabled",
40531     /**
40532      * @cfg {Date/String} minValue
40533      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40534      * valid format (defaults to null).
40535      */
40536     minValue : null,
40537     /**
40538      * @cfg {Date/String} maxValue
40539      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40540      * valid format (defaults to null).
40541      */
40542     maxValue : null,
40543     /**
40544      * @cfg {String} minText
40545      * The error text to display when the date in the cell is before minValue (defaults to
40546      * 'The date in this field must be after {minValue}').
40547      */
40548     minText : "The date in this field must be equal to or after {0}",
40549     /**
40550      * @cfg {String} maxTextf
40551      * The error text to display when the date in the cell is after maxValue (defaults to
40552      * 'The date in this field must be before {maxValue}').
40553      */
40554     maxText : "The date in this field must be equal to or before {0}",
40555     /**
40556      * @cfg {String} invalidText
40557      * The error text to display when the date in the field is invalid (defaults to
40558      * '{value} is not a valid date - it must be in the format {format}').
40559      */
40560     invalidText : "{0} is not a valid date - it must be in the format {1}",
40561     /**
40562      * @cfg {String} triggerClass
40563      * An additional CSS class used to style the trigger button.  The trigger will always get the
40564      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40565      * which displays a calendar icon).
40566      */
40567     triggerClass : 'x-form-date-trigger',
40568     
40569
40570     /**
40571      * @cfg {Boolean} useIso
40572      * if enabled, then the date field will use a hidden field to store the 
40573      * real value as iso formated date. default (true)
40574      */ 
40575     useIso : true,
40576     /**
40577      * @cfg {String/Object} autoCreate
40578      * A DomHelper element spec, or true for a default element spec (defaults to
40579      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40580      */ 
40581     // private
40582     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40583     
40584     // private
40585     hiddenField: false,
40586     
40587     hideMonthPicker : false,
40588     
40589     onRender : function(ct, position)
40590     {
40591         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40592         if (this.useIso) {
40593             this.el.dom.removeAttribute('name'); 
40594             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40595                     'before', true);
40596             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40597             // prevent input submission
40598             this.hiddenName = this.name;
40599         }
40600             
40601             
40602     },
40603     
40604     // private
40605     validateValue : function(value)
40606     {
40607         value = this.formatDate(value);
40608         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40609             return false;
40610         }
40611         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40612              return true;
40613         }
40614         var svalue = value;
40615         value = this.parseDate(value);
40616         if(!value){
40617             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40618             return false;
40619         }
40620         var time = value.getTime();
40621         if(this.minValue && time < this.minValue.getTime()){
40622             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40623             return false;
40624         }
40625         if(this.maxValue && time > this.maxValue.getTime()){
40626             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40627             return false;
40628         }
40629         /*if(this.disabledDays){
40630             var day = value.getDay();
40631             for(var i = 0; i < this.disabledDays.length; i++) {
40632                 if(day === this.disabledDays[i]){
40633                     this.markInvalid(this.disabledDaysText);
40634                     return false;
40635                 }
40636             }
40637         }
40638         */
40639         var fvalue = this.formatDate(value);
40640         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
40641             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40642             return false;
40643         }
40644         */
40645         return true;
40646     },
40647
40648     // private
40649     // Provides logic to override the default TriggerField.validateBlur which just returns true
40650     validateBlur : function(){
40651         return !this.menu || !this.menu.isVisible();
40652     },
40653
40654     /**
40655      * Returns the current date value of the date field.
40656      * @return {Date} The date value
40657      */
40658     getValue : function(){
40659         
40660         
40661         
40662         return  this.hiddenField ?
40663                 this.hiddenField.value :
40664                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
40665     },
40666
40667     /**
40668      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40669      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
40670      * (the default format used is "m/d/y").
40671      * <br />Usage:
40672      * <pre><code>
40673 //All of these calls set the same date value (May 4, 2006)
40674
40675 //Pass a date object:
40676 var dt = new Date('5/4/06');
40677 monthField.setValue(dt);
40678
40679 //Pass a date string (default format):
40680 monthField.setValue('5/4/06');
40681
40682 //Pass a date string (custom format):
40683 monthField.format = 'Y-m-d';
40684 monthField.setValue('2006-5-4');
40685 </code></pre>
40686      * @param {String/Date} date The date or valid date string
40687      */
40688     setValue : function(date){
40689         Roo.log('month setValue' + date);
40690         // can only be first of month..
40691         
40692         var val = this.parseDate(date);
40693         
40694         if (this.hiddenField) {
40695             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40696         }
40697         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40698         this.value = this.parseDate(date);
40699     },
40700
40701     // private
40702     parseDate : function(value){
40703         if(!value || value instanceof Date){
40704             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
40705             return value;
40706         }
40707         var v = Date.parseDate(value, this.format);
40708         if (!v && this.useIso) {
40709             v = Date.parseDate(value, 'Y-m-d');
40710         }
40711         if (v) {
40712             // 
40713             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
40714         }
40715         
40716         
40717         if(!v && this.altFormats){
40718             if(!this.altFormatsArray){
40719                 this.altFormatsArray = this.altFormats.split("|");
40720             }
40721             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40722                 v = Date.parseDate(value, this.altFormatsArray[i]);
40723             }
40724         }
40725         return v;
40726     },
40727
40728     // private
40729     formatDate : function(date, fmt){
40730         return (!date || !(date instanceof Date)) ?
40731                date : date.dateFormat(fmt || this.format);
40732     },
40733
40734     // private
40735     menuListeners : {
40736         select: function(m, d){
40737             this.setValue(d);
40738             this.fireEvent('select', this, d);
40739         },
40740         show : function(){ // retain focus styling
40741             this.onFocus();
40742         },
40743         hide : function(){
40744             this.focus.defer(10, this);
40745             var ml = this.menuListeners;
40746             this.menu.un("select", ml.select,  this);
40747             this.menu.un("show", ml.show,  this);
40748             this.menu.un("hide", ml.hide,  this);
40749         }
40750     },
40751     // private
40752     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40753     onTriggerClick : function(){
40754         if(this.disabled){
40755             return;
40756         }
40757         if(this.menu == null){
40758             this.menu = new Roo.menu.DateMenu();
40759            
40760         }
40761         
40762         Roo.apply(this.menu.picker,  {
40763             
40764             showClear: this.allowBlank,
40765             minDate : this.minValue,
40766             maxDate : this.maxValue,
40767             disabledDatesRE : this.ddMatch,
40768             disabledDatesText : this.disabledDatesText,
40769             
40770             format : this.useIso ? 'Y-m-d' : this.format,
40771             minText : String.format(this.minText, this.formatDate(this.minValue)),
40772             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40773             
40774         });
40775          this.menu.on(Roo.apply({}, this.menuListeners, {
40776             scope:this
40777         }));
40778        
40779         
40780         var m = this.menu;
40781         var p = m.picker;
40782         
40783         // hide month picker get's called when we called by 'before hide';
40784         
40785         var ignorehide = true;
40786         p.hideMonthPicker  = function(disableAnim){
40787             if (ignorehide) {
40788                 return;
40789             }
40790              if(this.monthPicker){
40791                 Roo.log("hideMonthPicker called");
40792                 if(disableAnim === true){
40793                     this.monthPicker.hide();
40794                 }else{
40795                     this.monthPicker.slideOut('t', {duration:.2});
40796                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
40797                     p.fireEvent("select", this, this.value);
40798                     m.hide();
40799                 }
40800             }
40801         }
40802         
40803         Roo.log('picker set value');
40804         Roo.log(this.getValue());
40805         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
40806         m.show(this.el, 'tl-bl?');
40807         ignorehide  = false;
40808         // this will trigger hideMonthPicker..
40809         
40810         
40811         // hidden the day picker
40812         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
40813         
40814         
40815         
40816       
40817         
40818         p.showMonthPicker.defer(100, p);
40819     
40820         
40821        
40822     },
40823
40824     beforeBlur : function(){
40825         var v = this.parseDate(this.getRawValue());
40826         if(v){
40827             this.setValue(v);
40828         }
40829     }
40830
40831     /** @cfg {Boolean} grow @hide */
40832     /** @cfg {Number} growMin @hide */
40833     /** @cfg {Number} growMax @hide */
40834     /**
40835      * @hide
40836      * @method autoSize
40837      */
40838 });/*
40839  * Based on:
40840  * Ext JS Library 1.1.1
40841  * Copyright(c) 2006-2007, Ext JS, LLC.
40842  *
40843  * Originally Released Under LGPL - original licence link has changed is not relivant.
40844  *
40845  * Fork - LGPL
40846  * <script type="text/javascript">
40847  */
40848  
40849
40850 /**
40851  * @class Roo.form.ComboBox
40852  * @extends Roo.form.TriggerField
40853  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
40854  * @constructor
40855  * Create a new ComboBox.
40856  * @param {Object} config Configuration options
40857  */
40858 Roo.form.ComboBox = function(config){
40859     Roo.form.ComboBox.superclass.constructor.call(this, config);
40860     this.addEvents({
40861         /**
40862          * @event expand
40863          * Fires when the dropdown list is expanded
40864              * @param {Roo.form.ComboBox} combo This combo box
40865              */
40866         'expand' : true,
40867         /**
40868          * @event collapse
40869          * Fires when the dropdown list is collapsed
40870              * @param {Roo.form.ComboBox} combo This combo box
40871              */
40872         'collapse' : true,
40873         /**
40874          * @event beforeselect
40875          * Fires before a list item is selected. Return false to cancel the selection.
40876              * @param {Roo.form.ComboBox} combo This combo box
40877              * @param {Roo.data.Record} record The data record returned from the underlying store
40878              * @param {Number} index The index of the selected item in the dropdown list
40879              */
40880         'beforeselect' : true,
40881         /**
40882          * @event select
40883          * Fires when a list item is selected
40884              * @param {Roo.form.ComboBox} combo This combo box
40885              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
40886              * @param {Number} index The index of the selected item in the dropdown list
40887              */
40888         'select' : true,
40889         /**
40890          * @event beforequery
40891          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
40892          * The event object passed has these properties:
40893              * @param {Roo.form.ComboBox} combo This combo box
40894              * @param {String} query The query
40895              * @param {Boolean} forceAll true to force "all" query
40896              * @param {Boolean} cancel true to cancel the query
40897              * @param {Object} e The query event object
40898              */
40899         'beforequery': true,
40900          /**
40901          * @event add
40902          * Fires when the 'add' icon is pressed (add a listener to enable add button)
40903              * @param {Roo.form.ComboBox} combo This combo box
40904              */
40905         'add' : true,
40906         /**
40907          * @event edit
40908          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
40909              * @param {Roo.form.ComboBox} combo This combo box
40910              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
40911              */
40912         'edit' : true
40913         
40914         
40915     });
40916     if(this.transform){
40917         this.allowDomMove = false;
40918         var s = Roo.getDom(this.transform);
40919         if(!this.hiddenName){
40920             this.hiddenName = s.name;
40921         }
40922         if(!this.store){
40923             this.mode = 'local';
40924             var d = [], opts = s.options;
40925             for(var i = 0, len = opts.length;i < len; i++){
40926                 var o = opts[i];
40927                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
40928                 if(o.selected) {
40929                     this.value = value;
40930                 }
40931                 d.push([value, o.text]);
40932             }
40933             this.store = new Roo.data.SimpleStore({
40934                 'id': 0,
40935                 fields: ['value', 'text'],
40936                 data : d
40937             });
40938             this.valueField = 'value';
40939             this.displayField = 'text';
40940         }
40941         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
40942         if(!this.lazyRender){
40943             this.target = true;
40944             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
40945             s.parentNode.removeChild(s); // remove it
40946             this.render(this.el.parentNode);
40947         }else{
40948             s.parentNode.removeChild(s); // remove it
40949         }
40950
40951     }
40952     if (this.store) {
40953         this.store = Roo.factory(this.store, Roo.data);
40954     }
40955     
40956     this.selectedIndex = -1;
40957     if(this.mode == 'local'){
40958         if(config.queryDelay === undefined){
40959             this.queryDelay = 10;
40960         }
40961         if(config.minChars === undefined){
40962             this.minChars = 0;
40963         }
40964     }
40965 };
40966
40967 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
40968     /**
40969      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
40970      */
40971     /**
40972      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
40973      * rendering into an Roo.Editor, defaults to false)
40974      */
40975     /**
40976      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
40977      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
40978      */
40979     /**
40980      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
40981      */
40982     /**
40983      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
40984      * the dropdown list (defaults to undefined, with no header element)
40985      */
40986
40987      /**
40988      * @cfg {String/Roo.Template} tpl The template to use to render the output
40989      */
40990      
40991     // private
40992     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
40993     /**
40994      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
40995      */
40996     listWidth: undefined,
40997     /**
40998      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
40999      * mode = 'remote' or 'text' if mode = 'local')
41000      */
41001     displayField: undefined,
41002     /**
41003      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41004      * mode = 'remote' or 'value' if mode = 'local'). 
41005      * Note: use of a valueField requires the user make a selection
41006      * in order for a value to be mapped.
41007      */
41008     valueField: undefined,
41009     
41010     
41011     /**
41012      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41013      * field's data value (defaults to the underlying DOM element's name)
41014      */
41015     hiddenName: undefined,
41016     /**
41017      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41018      */
41019     listClass: '',
41020     /**
41021      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41022      */
41023     selectedClass: 'x-combo-selected',
41024     /**
41025      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41026      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41027      * which displays a downward arrow icon).
41028      */
41029     triggerClass : 'x-form-arrow-trigger',
41030     /**
41031      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41032      */
41033     shadow:'sides',
41034     /**
41035      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41036      * anchor positions (defaults to 'tl-bl')
41037      */
41038     listAlign: 'tl-bl?',
41039     /**
41040      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41041      */
41042     maxHeight: 300,
41043     /**
41044      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41045      * query specified by the allQuery config option (defaults to 'query')
41046      */
41047     triggerAction: 'query',
41048     /**
41049      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41050      * (defaults to 4, does not apply if editable = false)
41051      */
41052     minChars : 4,
41053     /**
41054      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41055      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41056      */
41057     typeAhead: false,
41058     /**
41059      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41060      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41061      */
41062     queryDelay: 500,
41063     /**
41064      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41065      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41066      */
41067     pageSize: 0,
41068     /**
41069      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41070      * when editable = true (defaults to false)
41071      */
41072     selectOnFocus:false,
41073     /**
41074      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41075      */
41076     queryParam: 'query',
41077     /**
41078      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41079      * when mode = 'remote' (defaults to 'Loading...')
41080      */
41081     loadingText: 'Loading...',
41082     /**
41083      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41084      */
41085     resizable: false,
41086     /**
41087      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41088      */
41089     handleHeight : 8,
41090     /**
41091      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41092      * traditional select (defaults to true)
41093      */
41094     editable: true,
41095     /**
41096      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41097      */
41098     allQuery: '',
41099     /**
41100      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41101      */
41102     mode: 'remote',
41103     /**
41104      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41105      * listWidth has a higher value)
41106      */
41107     minListWidth : 70,
41108     /**
41109      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41110      * allow the user to set arbitrary text into the field (defaults to false)
41111      */
41112     forceSelection:false,
41113     /**
41114      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41115      * if typeAhead = true (defaults to 250)
41116      */
41117     typeAheadDelay : 250,
41118     /**
41119      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41120      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41121      */
41122     valueNotFoundText : undefined,
41123     /**
41124      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41125      */
41126     blockFocus : false,
41127     
41128     /**
41129      * @cfg {Boolean} disableClear Disable showing of clear button.
41130      */
41131     disableClear : false,
41132     /**
41133      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41134      */
41135     alwaysQuery : false,
41136     
41137     //private
41138     addicon : false,
41139     editicon: false,
41140     
41141     // element that contains real text value.. (when hidden is used..)
41142      
41143     // private
41144     onRender : function(ct, position){
41145         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41146         if(this.hiddenName){
41147             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41148                     'before', true);
41149             this.hiddenField.value =
41150                 this.hiddenValue !== undefined ? this.hiddenValue :
41151                 this.value !== undefined ? this.value : '';
41152
41153             // prevent input submission
41154             this.el.dom.removeAttribute('name');
41155              
41156              
41157         }
41158         if(Roo.isGecko){
41159             this.el.dom.setAttribute('autocomplete', 'off');
41160         }
41161
41162         var cls = 'x-combo-list';
41163
41164         this.list = new Roo.Layer({
41165             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41166         });
41167
41168         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41169         this.list.setWidth(lw);
41170         this.list.swallowEvent('mousewheel');
41171         this.assetHeight = 0;
41172
41173         if(this.title){
41174             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41175             this.assetHeight += this.header.getHeight();
41176         }
41177
41178         this.innerList = this.list.createChild({cls:cls+'-inner'});
41179         this.innerList.on('mouseover', this.onViewOver, this);
41180         this.innerList.on('mousemove', this.onViewMove, this);
41181         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41182         
41183         if(this.allowBlank && !this.pageSize && !this.disableClear){
41184             this.footer = this.list.createChild({cls:cls+'-ft'});
41185             this.pageTb = new Roo.Toolbar(this.footer);
41186            
41187         }
41188         if(this.pageSize){
41189             this.footer = this.list.createChild({cls:cls+'-ft'});
41190             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41191                     {pageSize: this.pageSize});
41192             
41193         }
41194         
41195         if (this.pageTb && this.allowBlank && !this.disableClear) {
41196             var _this = this;
41197             this.pageTb.add(new Roo.Toolbar.Fill(), {
41198                 cls: 'x-btn-icon x-btn-clear',
41199                 text: '&#160;',
41200                 handler: function()
41201                 {
41202                     _this.collapse();
41203                     _this.clearValue();
41204                     _this.onSelect(false, -1);
41205                 }
41206             });
41207         }
41208         if (this.footer) {
41209             this.assetHeight += this.footer.getHeight();
41210         }
41211         
41212
41213         if(!this.tpl){
41214             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41215         }
41216
41217         this.view = new Roo.View(this.innerList, this.tpl, {
41218             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41219         });
41220
41221         this.view.on('click', this.onViewClick, this);
41222
41223         this.store.on('beforeload', this.onBeforeLoad, this);
41224         this.store.on('load', this.onLoad, this);
41225         this.store.on('loadexception', this.onLoadException, this);
41226
41227         if(this.resizable){
41228             this.resizer = new Roo.Resizable(this.list,  {
41229                pinned:true, handles:'se'
41230             });
41231             this.resizer.on('resize', function(r, w, h){
41232                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41233                 this.listWidth = w;
41234                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41235                 this.restrictHeight();
41236             }, this);
41237             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41238         }
41239         if(!this.editable){
41240             this.editable = true;
41241             this.setEditable(false);
41242         }  
41243         
41244         
41245         if (typeof(this.events.add.listeners) != 'undefined') {
41246             
41247             this.addicon = this.wrap.createChild(
41248                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41249        
41250             this.addicon.on('click', function(e) {
41251                 this.fireEvent('add', this);
41252             }, this);
41253         }
41254         if (typeof(this.events.edit.listeners) != 'undefined') {
41255             
41256             this.editicon = this.wrap.createChild(
41257                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41258             if (this.addicon) {
41259                 this.editicon.setStyle('margin-left', '40px');
41260             }
41261             this.editicon.on('click', function(e) {
41262                 
41263                 // we fire even  if inothing is selected..
41264                 this.fireEvent('edit', this, this.lastData );
41265                 
41266             }, this);
41267         }
41268         
41269         
41270         
41271     },
41272
41273     // private
41274     initEvents : function(){
41275         Roo.form.ComboBox.superclass.initEvents.call(this);
41276
41277         this.keyNav = new Roo.KeyNav(this.el, {
41278             "up" : function(e){
41279                 this.inKeyMode = true;
41280                 this.selectPrev();
41281             },
41282
41283             "down" : function(e){
41284                 if(!this.isExpanded()){
41285                     this.onTriggerClick();
41286                 }else{
41287                     this.inKeyMode = true;
41288                     this.selectNext();
41289                 }
41290             },
41291
41292             "enter" : function(e){
41293                 this.onViewClick();
41294                 //return true;
41295             },
41296
41297             "esc" : function(e){
41298                 this.collapse();
41299             },
41300
41301             "tab" : function(e){
41302                 this.onViewClick(false);
41303                 this.fireEvent("specialkey", this, e);
41304                 return true;
41305             },
41306
41307             scope : this,
41308
41309             doRelay : function(foo, bar, hname){
41310                 if(hname == 'down' || this.scope.isExpanded()){
41311                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41312                 }
41313                 return true;
41314             },
41315
41316             forceKeyDown: true
41317         });
41318         this.queryDelay = Math.max(this.queryDelay || 10,
41319                 this.mode == 'local' ? 10 : 250);
41320         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41321         if(this.typeAhead){
41322             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41323         }
41324         if(this.editable !== false){
41325             this.el.on("keyup", this.onKeyUp, this);
41326         }
41327         if(this.forceSelection){
41328             this.on('blur', this.doForce, this);
41329         }
41330     },
41331
41332     onDestroy : function(){
41333         if(this.view){
41334             this.view.setStore(null);
41335             this.view.el.removeAllListeners();
41336             this.view.el.remove();
41337             this.view.purgeListeners();
41338         }
41339         if(this.list){
41340             this.list.destroy();
41341         }
41342         if(this.store){
41343             this.store.un('beforeload', this.onBeforeLoad, this);
41344             this.store.un('load', this.onLoad, this);
41345             this.store.un('loadexception', this.onLoadException, this);
41346         }
41347         Roo.form.ComboBox.superclass.onDestroy.call(this);
41348     },
41349
41350     // private
41351     fireKey : function(e){
41352         if(e.isNavKeyPress() && !this.list.isVisible()){
41353             this.fireEvent("specialkey", this, e);
41354         }
41355     },
41356
41357     // private
41358     onResize: function(w, h){
41359         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41360         
41361         if(typeof w != 'number'){
41362             // we do not handle it!?!?
41363             return;
41364         }
41365         var tw = this.trigger.getWidth();
41366         tw += this.addicon ? this.addicon.getWidth() : 0;
41367         tw += this.editicon ? this.editicon.getWidth() : 0;
41368         var x = w - tw;
41369         this.el.setWidth( this.adjustWidth('input', x));
41370             
41371         this.trigger.setStyle('left', x+'px');
41372         
41373         if(this.list && this.listWidth === undefined){
41374             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41375             this.list.setWidth(lw);
41376             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41377         }
41378         
41379     
41380         
41381     },
41382
41383     /**
41384      * Allow or prevent the user from directly editing the field text.  If false is passed,
41385      * the user will only be able to select from the items defined in the dropdown list.  This method
41386      * is the runtime equivalent of setting the 'editable' config option at config time.
41387      * @param {Boolean} value True to allow the user to directly edit the field text
41388      */
41389     setEditable : function(value){
41390         if(value == this.editable){
41391             return;
41392         }
41393         this.editable = value;
41394         if(!value){
41395             this.el.dom.setAttribute('readOnly', true);
41396             this.el.on('mousedown', this.onTriggerClick,  this);
41397             this.el.addClass('x-combo-noedit');
41398         }else{
41399             this.el.dom.setAttribute('readOnly', false);
41400             this.el.un('mousedown', this.onTriggerClick,  this);
41401             this.el.removeClass('x-combo-noedit');
41402         }
41403     },
41404
41405     // private
41406     onBeforeLoad : function(){
41407         if(!this.hasFocus){
41408             return;
41409         }
41410         this.innerList.update(this.loadingText ?
41411                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41412         this.restrictHeight();
41413         this.selectedIndex = -1;
41414     },
41415
41416     // private
41417     onLoad : function(){
41418         if(!this.hasFocus){
41419             return;
41420         }
41421         if(this.store.getCount() > 0){
41422             this.expand();
41423             this.restrictHeight();
41424             if(this.lastQuery == this.allQuery){
41425                 if(this.editable){
41426                     this.el.dom.select();
41427                 }
41428                 if(!this.selectByValue(this.value, true)){
41429                     this.select(0, true);
41430                 }
41431             }else{
41432                 this.selectNext();
41433                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41434                     this.taTask.delay(this.typeAheadDelay);
41435                 }
41436             }
41437         }else{
41438             this.onEmptyResults();
41439         }
41440         //this.el.focus();
41441     },
41442     // private
41443     onLoadException : function()
41444     {
41445         this.collapse();
41446         Roo.log(this.store.reader.jsonData);
41447         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41448             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41449         }
41450         
41451         
41452     },
41453     // private
41454     onTypeAhead : function(){
41455         if(this.store.getCount() > 0){
41456             var r = this.store.getAt(0);
41457             var newValue = r.data[this.displayField];
41458             var len = newValue.length;
41459             var selStart = this.getRawValue().length;
41460             if(selStart != len){
41461                 this.setRawValue(newValue);
41462                 this.selectText(selStart, newValue.length);
41463             }
41464         }
41465     },
41466
41467     // private
41468     onSelect : function(record, index){
41469         if(this.fireEvent('beforeselect', this, record, index) !== false){
41470             this.setFromData(index > -1 ? record.data : false);
41471             this.collapse();
41472             this.fireEvent('select', this, record, index);
41473         }
41474     },
41475
41476     /**
41477      * Returns the currently selected field value or empty string if no value is set.
41478      * @return {String} value The selected value
41479      */
41480     getValue : function(){
41481         if(this.valueField){
41482             return typeof this.value != 'undefined' ? this.value : '';
41483         }
41484         return Roo.form.ComboBox.superclass.getValue.call(this);
41485     },
41486
41487     /**
41488      * Clears any text/value currently set in the field
41489      */
41490     clearValue : function(){
41491         if(this.hiddenField){
41492             this.hiddenField.value = '';
41493         }
41494         this.value = '';
41495         this.setRawValue('');
41496         this.lastSelectionText = '';
41497         
41498     },
41499
41500     /**
41501      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41502      * will be displayed in the field.  If the value does not match the data value of an existing item,
41503      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41504      * Otherwise the field will be blank (although the value will still be set).
41505      * @param {String} value The value to match
41506      */
41507     setValue : function(v){
41508         var text = v;
41509         if(this.valueField){
41510             var r = this.findRecord(this.valueField, v);
41511             if(r){
41512                 text = r.data[this.displayField];
41513             }else if(this.valueNotFoundText !== undefined){
41514                 text = this.valueNotFoundText;
41515             }
41516         }
41517         this.lastSelectionText = text;
41518         if(this.hiddenField){
41519             this.hiddenField.value = v;
41520         }
41521         Roo.form.ComboBox.superclass.setValue.call(this, text);
41522         this.value = v;
41523     },
41524     /**
41525      * @property {Object} the last set data for the element
41526      */
41527     
41528     lastData : false,
41529     /**
41530      * Sets the value of the field based on a object which is related to the record format for the store.
41531      * @param {Object} value the value to set as. or false on reset?
41532      */
41533     setFromData : function(o){
41534         var dv = ''; // display value
41535         var vv = ''; // value value..
41536         this.lastData = o;
41537         if (this.displayField) {
41538             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41539         } else {
41540             // this is an error condition!!!
41541             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41542         }
41543         
41544         if(this.valueField){
41545             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41546         }
41547         if(this.hiddenField){
41548             this.hiddenField.value = vv;
41549             
41550             this.lastSelectionText = dv;
41551             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41552             this.value = vv;
41553             return;
41554         }
41555         // no hidden field.. - we store the value in 'value', but still display
41556         // display field!!!!
41557         this.lastSelectionText = dv;
41558         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41559         this.value = vv;
41560         
41561         
41562     },
41563     // private
41564     reset : function(){
41565         // overridden so that last data is reset..
41566         this.setValue(this.resetValue);
41567         this.clearInvalid();
41568         this.lastData = false;
41569         if (this.view) {
41570             this.view.clearSelections();
41571         }
41572     },
41573     // private
41574     findRecord : function(prop, value){
41575         var record;
41576         if(this.store.getCount() > 0){
41577             this.store.each(function(r){
41578                 if(r.data[prop] == value){
41579                     record = r;
41580                     return false;
41581                 }
41582                 return true;
41583             });
41584         }
41585         return record;
41586     },
41587     
41588     getName: function()
41589     {
41590         // returns hidden if it's set..
41591         if (!this.rendered) {return ''};
41592         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41593         
41594     },
41595     // private
41596     onViewMove : function(e, t){
41597         this.inKeyMode = false;
41598     },
41599
41600     // private
41601     onViewOver : function(e, t){
41602         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41603             return;
41604         }
41605         var item = this.view.findItemFromChild(t);
41606         if(item){
41607             var index = this.view.indexOf(item);
41608             this.select(index, false);
41609         }
41610     },
41611
41612     // private
41613     onViewClick : function(doFocus)
41614     {
41615         var index = this.view.getSelectedIndexes()[0];
41616         var r = this.store.getAt(index);
41617         if(r){
41618             this.onSelect(r, index);
41619         }
41620         if(doFocus !== false && !this.blockFocus){
41621             this.el.focus();
41622         }
41623     },
41624
41625     // private
41626     restrictHeight : function(){
41627         this.innerList.dom.style.height = '';
41628         var inner = this.innerList.dom;
41629         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41630         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41631         this.list.beginUpdate();
41632         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41633         this.list.alignTo(this.el, this.listAlign);
41634         this.list.endUpdate();
41635     },
41636
41637     // private
41638     onEmptyResults : function(){
41639         this.collapse();
41640     },
41641
41642     /**
41643      * Returns true if the dropdown list is expanded, else false.
41644      */
41645     isExpanded : function(){
41646         return this.list.isVisible();
41647     },
41648
41649     /**
41650      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
41651      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41652      * @param {String} value The data value of the item to select
41653      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41654      * selected item if it is not currently in view (defaults to true)
41655      * @return {Boolean} True if the value matched an item in the list, else false
41656      */
41657     selectByValue : function(v, scrollIntoView){
41658         if(v !== undefined && v !== null){
41659             var r = this.findRecord(this.valueField || this.displayField, v);
41660             if(r){
41661                 this.select(this.store.indexOf(r), scrollIntoView);
41662                 return true;
41663             }
41664         }
41665         return false;
41666     },
41667
41668     /**
41669      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
41670      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41671      * @param {Number} index The zero-based index of the list item to select
41672      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41673      * selected item if it is not currently in view (defaults to true)
41674      */
41675     select : function(index, scrollIntoView){
41676         this.selectedIndex = index;
41677         this.view.select(index);
41678         if(scrollIntoView !== false){
41679             var el = this.view.getNode(index);
41680             if(el){
41681                 this.innerList.scrollChildIntoView(el, false);
41682             }
41683         }
41684     },
41685
41686     // private
41687     selectNext : function(){
41688         var ct = this.store.getCount();
41689         if(ct > 0){
41690             if(this.selectedIndex == -1){
41691                 this.select(0);
41692             }else if(this.selectedIndex < ct-1){
41693                 this.select(this.selectedIndex+1);
41694             }
41695         }
41696     },
41697
41698     // private
41699     selectPrev : function(){
41700         var ct = this.store.getCount();
41701         if(ct > 0){
41702             if(this.selectedIndex == -1){
41703                 this.select(0);
41704             }else if(this.selectedIndex != 0){
41705                 this.select(this.selectedIndex-1);
41706             }
41707         }
41708     },
41709
41710     // private
41711     onKeyUp : function(e){
41712         if(this.editable !== false && !e.isSpecialKey()){
41713             this.lastKey = e.getKey();
41714             this.dqTask.delay(this.queryDelay);
41715         }
41716     },
41717
41718     // private
41719     validateBlur : function(){
41720         return !this.list || !this.list.isVisible();   
41721     },
41722
41723     // private
41724     initQuery : function(){
41725         this.doQuery(this.getRawValue());
41726     },
41727
41728     // private
41729     doForce : function(){
41730         if(this.el.dom.value.length > 0){
41731             this.el.dom.value =
41732                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
41733              
41734         }
41735     },
41736
41737     /**
41738      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
41739      * query allowing the query action to be canceled if needed.
41740      * @param {String} query The SQL query to execute
41741      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
41742      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
41743      * saved in the current store (defaults to false)
41744      */
41745     doQuery : function(q, forceAll){
41746         if(q === undefined || q === null){
41747             q = '';
41748         }
41749         var qe = {
41750             query: q,
41751             forceAll: forceAll,
41752             combo: this,
41753             cancel:false
41754         };
41755         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
41756             return false;
41757         }
41758         q = qe.query;
41759         forceAll = qe.forceAll;
41760         if(forceAll === true || (q.length >= this.minChars)){
41761             if(this.lastQuery != q || this.alwaysQuery){
41762                 this.lastQuery = q;
41763                 if(this.mode == 'local'){
41764                     this.selectedIndex = -1;
41765                     if(forceAll){
41766                         this.store.clearFilter();
41767                     }else{
41768                         this.store.filter(this.displayField, q);
41769                     }
41770                     this.onLoad();
41771                 }else{
41772                     this.store.baseParams[this.queryParam] = q;
41773                     this.store.load({
41774                         params: this.getParams(q)
41775                     });
41776                     this.expand();
41777                 }
41778             }else{
41779                 this.selectedIndex = -1;
41780                 this.onLoad();   
41781             }
41782         }
41783     },
41784
41785     // private
41786     getParams : function(q){
41787         var p = {};
41788         //p[this.queryParam] = q;
41789         if(this.pageSize){
41790             p.start = 0;
41791             p.limit = this.pageSize;
41792         }
41793         return p;
41794     },
41795
41796     /**
41797      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
41798      */
41799     collapse : function(){
41800         if(!this.isExpanded()){
41801             return;
41802         }
41803         this.list.hide();
41804         Roo.get(document).un('mousedown', this.collapseIf, this);
41805         Roo.get(document).un('mousewheel', this.collapseIf, this);
41806         if (!this.editable) {
41807             Roo.get(document).un('keydown', this.listKeyPress, this);
41808         }
41809         this.fireEvent('collapse', this);
41810     },
41811
41812     // private
41813     collapseIf : function(e){
41814         if(!e.within(this.wrap) && !e.within(this.list)){
41815             this.collapse();
41816         }
41817     },
41818
41819     /**
41820      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
41821      */
41822     expand : function(){
41823         if(this.isExpanded() || !this.hasFocus){
41824             return;
41825         }
41826         this.list.alignTo(this.el, this.listAlign);
41827         this.list.show();
41828         Roo.get(document).on('mousedown', this.collapseIf, this);
41829         Roo.get(document).on('mousewheel', this.collapseIf, this);
41830         if (!this.editable) {
41831             Roo.get(document).on('keydown', this.listKeyPress, this);
41832         }
41833         
41834         this.fireEvent('expand', this);
41835     },
41836
41837     // private
41838     // Implements the default empty TriggerField.onTriggerClick function
41839     onTriggerClick : function(){
41840         if(this.disabled){
41841             return;
41842         }
41843         if(this.isExpanded()){
41844             this.collapse();
41845             if (!this.blockFocus) {
41846                 this.el.focus();
41847             }
41848             
41849         }else {
41850             this.hasFocus = true;
41851             if(this.triggerAction == 'all') {
41852                 this.doQuery(this.allQuery, true);
41853             } else {
41854                 this.doQuery(this.getRawValue());
41855             }
41856             if (!this.blockFocus) {
41857                 this.el.focus();
41858             }
41859         }
41860     },
41861     listKeyPress : function(e)
41862     {
41863         //Roo.log('listkeypress');
41864         // scroll to first matching element based on key pres..
41865         if (e.isSpecialKey()) {
41866             return false;
41867         }
41868         var k = String.fromCharCode(e.getKey()).toUpperCase();
41869         //Roo.log(k);
41870         var match  = false;
41871         var csel = this.view.getSelectedNodes();
41872         var cselitem = false;
41873         if (csel.length) {
41874             var ix = this.view.indexOf(csel[0]);
41875             cselitem  = this.store.getAt(ix);
41876             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
41877                 cselitem = false;
41878             }
41879             
41880         }
41881         
41882         this.store.each(function(v) { 
41883             if (cselitem) {
41884                 // start at existing selection.
41885                 if (cselitem.id == v.id) {
41886                     cselitem = false;
41887                 }
41888                 return;
41889             }
41890                 
41891             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
41892                 match = this.store.indexOf(v);
41893                 return false;
41894             }
41895         }, this);
41896         
41897         if (match === false) {
41898             return true; // no more action?
41899         }
41900         // scroll to?
41901         this.view.select(match);
41902         var sn = Roo.get(this.view.getSelectedNodes()[0]);
41903         sn.scrollIntoView(sn.dom.parentNode, false);
41904     }
41905
41906     /** 
41907     * @cfg {Boolean} grow 
41908     * @hide 
41909     */
41910     /** 
41911     * @cfg {Number} growMin 
41912     * @hide 
41913     */
41914     /** 
41915     * @cfg {Number} growMax 
41916     * @hide 
41917     */
41918     /**
41919      * @hide
41920      * @method autoSize
41921      */
41922 });/*
41923  * Copyright(c) 2010-2012, Roo J Solutions Limited
41924  *
41925  * Licence LGPL
41926  *
41927  */
41928
41929 /**
41930  * @class Roo.form.ComboBoxArray
41931  * @extends Roo.form.TextField
41932  * A facebook style adder... for lists of email / people / countries  etc...
41933  * pick multiple items from a combo box, and shows each one.
41934  *
41935  *  Fred [x]  Brian [x]  [Pick another |v]
41936  *
41937  *
41938  *  For this to work: it needs various extra information
41939  *    - normal combo problay has
41940  *      name, hiddenName
41941  *    + displayField, valueField
41942  *
41943  *    For our purpose...
41944  *
41945  *
41946  *   If we change from 'extends' to wrapping...
41947  *   
41948  *  
41949  *
41950  
41951  
41952  * @constructor
41953  * Create a new ComboBoxArray.
41954  * @param {Object} config Configuration options
41955  */
41956  
41957
41958 Roo.form.ComboBoxArray = function(config)
41959 {
41960     this.addEvents({
41961         /**
41962          * @event beforeremove
41963          * Fires before remove the value from the list
41964              * @param {Roo.form.ComboBoxArray} _self This combo box array
41965              * @param {Roo.form.ComboBoxArray.Item} item removed item
41966              */
41967         'beforeremove' : true,
41968         /**
41969          * @event remove
41970          * Fires when remove the value from the list
41971              * @param {Roo.form.ComboBoxArray} _self This combo box array
41972              * @param {Roo.form.ComboBoxArray.Item} item removed item
41973              */
41974         'remove' : true
41975         
41976         
41977     });
41978     
41979     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
41980     
41981     this.items = new Roo.util.MixedCollection(false);
41982     
41983     // construct the child combo...
41984     
41985     
41986     
41987     
41988    
41989     
41990 }
41991
41992  
41993 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
41994
41995     /**
41996      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
41997      */
41998     
41999     lastData : false,
42000     
42001     // behavies liek a hiddne field
42002     inputType:      'hidden',
42003     /**
42004      * @cfg {Number} width The width of the box that displays the selected element
42005      */ 
42006     width:          300,
42007
42008     
42009     
42010     /**
42011      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42012      */
42013     name : false,
42014     /**
42015      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42016      */
42017     hiddenName : false,
42018     
42019     
42020     // private the array of items that are displayed..
42021     items  : false,
42022     // private - the hidden field el.
42023     hiddenEl : false,
42024     // private - the filed el..
42025     el : false,
42026     
42027     //validateValue : function() { return true; }, // all values are ok!
42028     //onAddClick: function() { },
42029     
42030     onRender : function(ct, position) 
42031     {
42032         
42033         // create the standard hidden element
42034         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42035         
42036         
42037         // give fake names to child combo;
42038         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42039         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
42040         
42041         this.combo = Roo.factory(this.combo, Roo.form);
42042         this.combo.onRender(ct, position);
42043         if (typeof(this.combo.width) != 'undefined') {
42044             this.combo.onResize(this.combo.width,0);
42045         }
42046         
42047         this.combo.initEvents();
42048         
42049         // assigned so form know we need to do this..
42050         this.store          = this.combo.store;
42051         this.valueField     = this.combo.valueField;
42052         this.displayField   = this.combo.displayField ;
42053         
42054         
42055         this.combo.wrap.addClass('x-cbarray-grp');
42056         
42057         var cbwrap = this.combo.wrap.createChild(
42058             {tag: 'div', cls: 'x-cbarray-cb'},
42059             this.combo.el.dom
42060         );
42061         
42062              
42063         this.hiddenEl = this.combo.wrap.createChild({
42064             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42065         });
42066         this.el = this.combo.wrap.createChild({
42067             tag: 'input',  type:'hidden' , name: this.name, value : ''
42068         });
42069          //   this.el.dom.removeAttribute("name");
42070         
42071         
42072         this.outerWrap = this.combo.wrap;
42073         this.wrap = cbwrap;
42074         
42075         this.outerWrap.setWidth(this.width);
42076         this.outerWrap.dom.removeChild(this.el.dom);
42077         
42078         this.wrap.dom.appendChild(this.el.dom);
42079         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42080         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42081         
42082         this.combo.trigger.setStyle('position','relative');
42083         this.combo.trigger.setStyle('left', '0px');
42084         this.combo.trigger.setStyle('top', '2px');
42085         
42086         this.combo.el.setStyle('vertical-align', 'text-bottom');
42087         
42088         //this.trigger.setStyle('vertical-align', 'top');
42089         
42090         // this should use the code from combo really... on('add' ....)
42091         if (this.adder) {
42092             
42093         
42094             this.adder = this.outerWrap.createChild(
42095                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42096             var _t = this;
42097             this.adder.on('click', function(e) {
42098                 _t.fireEvent('adderclick', this, e);
42099             }, _t);
42100         }
42101         //var _t = this;
42102         //this.adder.on('click', this.onAddClick, _t);
42103         
42104         
42105         this.combo.on('select', function(cb, rec, ix) {
42106             this.addItem(rec.data);
42107             
42108             cb.setValue('');
42109             cb.el.dom.value = '';
42110             //cb.lastData = rec.data;
42111             // add to list
42112             
42113         }, this);
42114         
42115         
42116     },
42117     
42118     
42119     getName: function()
42120     {
42121         // returns hidden if it's set..
42122         if (!this.rendered) {return ''};
42123         return  this.hiddenName ? this.hiddenName : this.name;
42124         
42125     },
42126     
42127     
42128     onResize: function(w, h){
42129         
42130         return;
42131         // not sure if this is needed..
42132         //this.combo.onResize(w,h);
42133         
42134         if(typeof w != 'number'){
42135             // we do not handle it!?!?
42136             return;
42137         }
42138         var tw = this.combo.trigger.getWidth();
42139         tw += this.addicon ? this.addicon.getWidth() : 0;
42140         tw += this.editicon ? this.editicon.getWidth() : 0;
42141         var x = w - tw;
42142         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42143             
42144         this.combo.trigger.setStyle('left', '0px');
42145         
42146         if(this.list && this.listWidth === undefined){
42147             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42148             this.list.setWidth(lw);
42149             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42150         }
42151         
42152     
42153         
42154     },
42155     
42156     addItem: function(rec)
42157     {
42158         var valueField = this.combo.valueField;
42159         var displayField = this.combo.displayField;
42160         if (this.items.indexOfKey(rec[valueField]) > -1) {
42161             //console.log("GOT " + rec.data.id);
42162             return;
42163         }
42164         
42165         var x = new Roo.form.ComboBoxArray.Item({
42166             //id : rec[this.idField],
42167             data : rec,
42168             displayField : displayField ,
42169             tipField : displayField ,
42170             cb : this
42171         });
42172         // use the 
42173         this.items.add(rec[valueField],x);
42174         // add it before the element..
42175         this.updateHiddenEl();
42176         x.render(this.outerWrap, this.wrap.dom);
42177         // add the image handler..
42178     },
42179     
42180     updateHiddenEl : function()
42181     {
42182         this.validate();
42183         if (!this.hiddenEl) {
42184             return;
42185         }
42186         var ar = [];
42187         var idField = this.combo.valueField;
42188         
42189         this.items.each(function(f) {
42190             ar.push(f.data[idField]);
42191            
42192         });
42193         this.hiddenEl.dom.value = ar.join(',');
42194         this.validate();
42195     },
42196     
42197     reset : function()
42198     {
42199         this.items.clear();
42200         
42201         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42202            el.remove();
42203         });
42204         
42205         this.el.dom.value = '';
42206         if (this.hiddenEl) {
42207             this.hiddenEl.dom.value = '';
42208         }
42209         
42210     },
42211     getValue: function()
42212     {
42213         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42214     },
42215     setValue: function(v) // not a valid action - must use addItems..
42216     {
42217          
42218         this.reset();
42219         
42220         
42221         
42222         if (this.store.isLocal && (typeof(v) == 'string')) {
42223             // then we can use the store to find the values..
42224             // comma seperated at present.. this needs to allow JSON based encoding..
42225             this.hiddenEl.value  = v;
42226             var v_ar = [];
42227             Roo.each(v.split(','), function(k) {
42228                 Roo.log("CHECK " + this.valueField + ',' + k);
42229                 var li = this.store.query(this.valueField, k);
42230                 if (!li.length) {
42231                     return;
42232                 }
42233                 var add = {};
42234                 add[this.valueField] = k;
42235                 add[this.displayField] = li.item(0).data[this.displayField];
42236                 
42237                 this.addItem(add);
42238             }, this) 
42239              
42240         }
42241         if (typeof(v) == 'object' ) {
42242             // then let's assume it's an array of objects..
42243             Roo.each(v, function(l) {
42244                 this.addItem(l);
42245             }, this);
42246              
42247         }
42248         
42249         
42250     },
42251     setFromData: function(v)
42252     {
42253         // this recieves an object, if setValues is called.
42254         this.reset();
42255         this.el.dom.value = v[this.displayField];
42256         this.hiddenEl.dom.value = v[this.valueField];
42257         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42258             return;
42259         }
42260         var kv = v[this.valueField];
42261         var dv = v[this.displayField];
42262         kv = typeof(kv) != 'string' ? '' : kv;
42263         dv = typeof(dv) != 'string' ? '' : dv;
42264         
42265         
42266         var keys = kv.split(',');
42267         var display = dv.split(',');
42268         for (var i = 0 ; i < keys.length; i++) {
42269             
42270             add = {};
42271             add[this.valueField] = keys[i];
42272             add[this.displayField] = display[i];
42273             this.addItem(add);
42274         }
42275       
42276         
42277     },
42278     
42279     /**
42280      * Validates the combox array value
42281      * @return {Boolean} True if the value is valid, else false
42282      */
42283     validate : function(){
42284         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42285             this.clearInvalid();
42286             return true;
42287         }
42288         return false;
42289     },
42290     
42291     validateValue : function(value){
42292         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42293         
42294     },
42295     
42296     /*@
42297      * overide
42298      * 
42299      */
42300     isDirty : function() {
42301         if(this.disabled) {
42302             return false;
42303         }
42304         
42305         try {
42306             var d = Roo.decode(String(this.originalValue));
42307         } catch (e) {
42308             return String(this.getValue()) !== String(this.originalValue);
42309         }
42310         
42311         var originalValue = [];
42312         
42313         for (var i = 0; i < d.length; i++){
42314             originalValue.push(d[i][this.valueField]);
42315         }
42316         
42317         return String(this.getValue()) !== String(originalValue.join(','));
42318         
42319     }
42320     
42321 });
42322
42323
42324
42325 /**
42326  * @class Roo.form.ComboBoxArray.Item
42327  * @extends Roo.BoxComponent
42328  * A selected item in the list
42329  *  Fred [x]  Brian [x]  [Pick another |v]
42330  * 
42331  * @constructor
42332  * Create a new item.
42333  * @param {Object} config Configuration options
42334  */
42335  
42336 Roo.form.ComboBoxArray.Item = function(config) {
42337     config.id = Roo.id();
42338     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42339 }
42340
42341 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42342     data : {},
42343     cb: false,
42344     displayField : false,
42345     tipField : false,
42346     
42347     
42348     defaultAutoCreate : {
42349         tag: 'div',
42350         cls: 'x-cbarray-item',
42351         cn : [ 
42352             { tag: 'div' },
42353             {
42354                 tag: 'img',
42355                 width:16,
42356                 height : 16,
42357                 src : Roo.BLANK_IMAGE_URL ,
42358                 align: 'center'
42359             }
42360         ]
42361         
42362     },
42363     
42364  
42365     onRender : function(ct, position)
42366     {
42367         Roo.form.Field.superclass.onRender.call(this, ct, position);
42368         
42369         if(!this.el){
42370             var cfg = this.getAutoCreate();
42371             this.el = ct.createChild(cfg, position);
42372         }
42373         
42374         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42375         
42376         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42377             this.cb.renderer(this.data) :
42378             String.format('{0}',this.data[this.displayField]);
42379         
42380             
42381         this.el.child('div').dom.setAttribute('qtip',
42382                         String.format('{0}',this.data[this.tipField])
42383         );
42384         
42385         this.el.child('img').on('click', this.remove, this);
42386         
42387     },
42388    
42389     remove : function()
42390     {
42391         if(this.cb.disabled){
42392             return;
42393         }
42394         
42395         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42396             this.cb.items.remove(this);
42397             this.el.child('img').un('click', this.remove, this);
42398             this.el.remove();
42399             this.cb.updateHiddenEl();
42400
42401             this.cb.fireEvent('remove', this.cb, this);
42402         }
42403         
42404     }
42405 });/*
42406  * Based on:
42407  * Ext JS Library 1.1.1
42408  * Copyright(c) 2006-2007, Ext JS, LLC.
42409  *
42410  * Originally Released Under LGPL - original licence link has changed is not relivant.
42411  *
42412  * Fork - LGPL
42413  * <script type="text/javascript">
42414  */
42415 /**
42416  * @class Roo.form.Checkbox
42417  * @extends Roo.form.Field
42418  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
42419  * @constructor
42420  * Creates a new Checkbox
42421  * @param {Object} config Configuration options
42422  */
42423 Roo.form.Checkbox = function(config){
42424     Roo.form.Checkbox.superclass.constructor.call(this, config);
42425     this.addEvents({
42426         /**
42427          * @event check
42428          * Fires when the checkbox is checked or unchecked.
42429              * @param {Roo.form.Checkbox} this This checkbox
42430              * @param {Boolean} checked The new checked value
42431              */
42432         check : true
42433     });
42434 };
42435
42436 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
42437     /**
42438      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42439      */
42440     focusClass : undefined,
42441     /**
42442      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42443      */
42444     fieldClass: "x-form-field",
42445     /**
42446      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
42447      */
42448     checked: false,
42449     /**
42450      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42451      * {tag: "input", type: "checkbox", autocomplete: "off"})
42452      */
42453     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
42454     /**
42455      * @cfg {String} boxLabel The text that appears beside the checkbox
42456      */
42457     boxLabel : "",
42458     /**
42459      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
42460      */  
42461     inputValue : '1',
42462     /**
42463      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
42464      */
42465      valueOff: '0', // value when not checked..
42466
42467     actionMode : 'viewEl', 
42468     //
42469     // private
42470     itemCls : 'x-menu-check-item x-form-item',
42471     groupClass : 'x-menu-group-item',
42472     inputType : 'hidden',
42473     
42474     
42475     inSetChecked: false, // check that we are not calling self...
42476     
42477     inputElement: false, // real input element?
42478     basedOn: false, // ????
42479     
42480     isFormField: true, // not sure where this is needed!!!!
42481
42482     onResize : function(){
42483         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
42484         if(!this.boxLabel){
42485             this.el.alignTo(this.wrap, 'c-c');
42486         }
42487     },
42488
42489     initEvents : function(){
42490         Roo.form.Checkbox.superclass.initEvents.call(this);
42491         this.el.on("click", this.onClick,  this);
42492         this.el.on("change", this.onClick,  this);
42493     },
42494
42495
42496     getResizeEl : function(){
42497         return this.wrap;
42498     },
42499
42500     getPositionEl : function(){
42501         return this.wrap;
42502     },
42503
42504     // private
42505     onRender : function(ct, position){
42506         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42507         /*
42508         if(this.inputValue !== undefined){
42509             this.el.dom.value = this.inputValue;
42510         }
42511         */
42512         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42513         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42514         var viewEl = this.wrap.createChild({ 
42515             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42516         this.viewEl = viewEl;   
42517         this.wrap.on('click', this.onClick,  this); 
42518         
42519         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42520         this.el.on('propertychange', this.setFromHidden,  this);  //ie
42521         
42522         
42523         
42524         if(this.boxLabel){
42525             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42526         //    viewEl.on('click', this.onClick,  this); 
42527         }
42528         //if(this.checked){
42529             this.setChecked(this.checked);
42530         //}else{
42531             //this.checked = this.el.dom;
42532         //}
42533
42534     },
42535
42536     // private
42537     initValue : Roo.emptyFn,
42538
42539     /**
42540      * Returns the checked state of the checkbox.
42541      * @return {Boolean} True if checked, else false
42542      */
42543     getValue : function(){
42544         if(this.el){
42545             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
42546         }
42547         return this.valueOff;
42548         
42549     },
42550
42551         // private
42552     onClick : function(){ 
42553         if (this.disabled) {
42554             return;
42555         }
42556         this.setChecked(!this.checked);
42557
42558         //if(this.el.dom.checked != this.checked){
42559         //    this.setValue(this.el.dom.checked);
42560        // }
42561     },
42562
42563     /**
42564      * Sets the checked state of the checkbox.
42565      * On is always based on a string comparison between inputValue and the param.
42566      * @param {Boolean/String} value - the value to set 
42567      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
42568      */
42569     setValue : function(v,suppressEvent){
42570         
42571         
42572         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
42573         //if(this.el && this.el.dom){
42574         //    this.el.dom.checked = this.checked;
42575         //    this.el.dom.defaultChecked = this.checked;
42576         //}
42577         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
42578         //this.fireEvent("check", this, this.checked);
42579     },
42580     // private..
42581     setChecked : function(state,suppressEvent)
42582     {
42583         if (this.inSetChecked) {
42584             this.checked = state;
42585             return;
42586         }
42587         
42588     
42589         if(this.wrap){
42590             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
42591         }
42592         this.checked = state;
42593         if(suppressEvent !== true){
42594             this.fireEvent('check', this, state);
42595         }
42596         this.inSetChecked = true;
42597         this.el.dom.value = state ? this.inputValue : this.valueOff;
42598         this.inSetChecked = false;
42599         
42600     },
42601     // handle setting of hidden value by some other method!!?!?
42602     setFromHidden: function()
42603     {
42604         if(!this.el){
42605             return;
42606         }
42607         //console.log("SET FROM HIDDEN");
42608         //alert('setFrom hidden');
42609         this.setValue(this.el.dom.value);
42610     },
42611     
42612     onDestroy : function()
42613     {
42614         if(this.viewEl){
42615             Roo.get(this.viewEl).remove();
42616         }
42617          
42618         Roo.form.Checkbox.superclass.onDestroy.call(this);
42619     },
42620     
42621     setBoxLabel : function(str)
42622     {
42623         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
42624     }
42625
42626 });/*
42627  * Based on:
42628  * Ext JS Library 1.1.1
42629  * Copyright(c) 2006-2007, Ext JS, LLC.
42630  *
42631  * Originally Released Under LGPL - original licence link has changed is not relivant.
42632  *
42633  * Fork - LGPL
42634  * <script type="text/javascript">
42635  */
42636  
42637 /**
42638  * @class Roo.form.Radio
42639  * @extends Roo.form.Checkbox
42640  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
42641  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
42642  * @constructor
42643  * Creates a new Radio
42644  * @param {Object} config Configuration options
42645  */
42646 Roo.form.Radio = function(){
42647     Roo.form.Radio.superclass.constructor.apply(this, arguments);
42648 };
42649 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
42650     inputType: 'radio',
42651
42652     /**
42653      * If this radio is part of a group, it will return the selected value
42654      * @return {String}
42655      */
42656     getGroupValue : function(){
42657         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
42658     },
42659     
42660     
42661     onRender : function(ct, position){
42662         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42663         
42664         if(this.inputValue !== undefined){
42665             this.el.dom.value = this.inputValue;
42666         }
42667          
42668         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42669         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42670         //var viewEl = this.wrap.createChild({ 
42671         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42672         //this.viewEl = viewEl;   
42673         //this.wrap.on('click', this.onClick,  this); 
42674         
42675         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42676         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
42677         
42678         
42679         
42680         if(this.boxLabel){
42681             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42682         //    viewEl.on('click', this.onClick,  this); 
42683         }
42684          if(this.checked){
42685             this.el.dom.checked =   'checked' ;
42686         }
42687          
42688     } 
42689     
42690     
42691 });//<script type="text/javascript">
42692
42693 /*
42694  * Based  Ext JS Library 1.1.1
42695  * Copyright(c) 2006-2007, Ext JS, LLC.
42696  * LGPL
42697  *
42698  */
42699  
42700 /**
42701  * @class Roo.HtmlEditorCore
42702  * @extends Roo.Component
42703  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
42704  *
42705  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42706  */
42707
42708 Roo.HtmlEditorCore = function(config){
42709     
42710     
42711     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
42712     
42713     
42714     this.addEvents({
42715         /**
42716          * @event initialize
42717          * Fires when the editor is fully initialized (including the iframe)
42718          * @param {Roo.HtmlEditorCore} this
42719          */
42720         initialize: true,
42721         /**
42722          * @event activate
42723          * Fires when the editor is first receives the focus. Any insertion must wait
42724          * until after this event.
42725          * @param {Roo.HtmlEditorCore} this
42726          */
42727         activate: true,
42728          /**
42729          * @event beforesync
42730          * Fires before the textarea is updated with content from the editor iframe. Return false
42731          * to cancel the sync.
42732          * @param {Roo.HtmlEditorCore} this
42733          * @param {String} html
42734          */
42735         beforesync: true,
42736          /**
42737          * @event beforepush
42738          * Fires before the iframe editor is updated with content from the textarea. Return false
42739          * to cancel the push.
42740          * @param {Roo.HtmlEditorCore} this
42741          * @param {String} html
42742          */
42743         beforepush: true,
42744          /**
42745          * @event sync
42746          * Fires when the textarea is updated with content from the editor iframe.
42747          * @param {Roo.HtmlEditorCore} this
42748          * @param {String} html
42749          */
42750         sync: true,
42751          /**
42752          * @event push
42753          * Fires when the iframe editor is updated with content from the textarea.
42754          * @param {Roo.HtmlEditorCore} this
42755          * @param {String} html
42756          */
42757         push: true,
42758         
42759         /**
42760          * @event editorevent
42761          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42762          * @param {Roo.HtmlEditorCore} this
42763          */
42764         editorevent: true
42765         
42766     });
42767     
42768     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
42769     
42770     // defaults : white / black...
42771     this.applyBlacklists();
42772     
42773     
42774     
42775 };
42776
42777
42778 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
42779
42780
42781      /**
42782      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
42783      */
42784     
42785     owner : false,
42786     
42787      /**
42788      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42789      *                        Roo.resizable.
42790      */
42791     resizable : false,
42792      /**
42793      * @cfg {Number} height (in pixels)
42794      */   
42795     height: 300,
42796    /**
42797      * @cfg {Number} width (in pixels)
42798      */   
42799     width: 500,
42800     
42801     /**
42802      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42803      * 
42804      */
42805     stylesheets: false,
42806     
42807     // id of frame..
42808     frameId: false,
42809     
42810     // private properties
42811     validationEvent : false,
42812     deferHeight: true,
42813     initialized : false,
42814     activated : false,
42815     sourceEditMode : false,
42816     onFocus : Roo.emptyFn,
42817     iframePad:3,
42818     hideMode:'offsets',
42819     
42820     clearUp: true,
42821     
42822     // blacklist + whitelisted elements..
42823     black: false,
42824     white: false,
42825      
42826     
42827
42828     /**
42829      * Protected method that will not generally be called directly. It
42830      * is called when the editor initializes the iframe with HTML contents. Override this method if you
42831      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
42832      */
42833     getDocMarkup : function(){
42834         // body styles..
42835         var st = '';
42836         
42837         // inherit styels from page...?? 
42838         if (this.stylesheets === false) {
42839             
42840             Roo.get(document.head).select('style').each(function(node) {
42841                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42842             });
42843             
42844             Roo.get(document.head).select('link').each(function(node) { 
42845                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42846             });
42847             
42848         } else if (!this.stylesheets.length) {
42849                 // simple..
42850                 st = '<style type="text/css">' +
42851                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42852                    '</style>';
42853         } else { 
42854             
42855         }
42856         
42857         st +=  '<style type="text/css">' +
42858             'IMG { cursor: pointer } ' +
42859         '</style>';
42860
42861         
42862         return '<html><head>' + st  +
42863             //<style type="text/css">' +
42864             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42865             //'</style>' +
42866             ' </head><body class="roo-htmleditor-body"></body></html>';
42867     },
42868
42869     // private
42870     onRender : function(ct, position)
42871     {
42872         var _t = this;
42873         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
42874         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
42875         
42876         
42877         this.el.dom.style.border = '0 none';
42878         this.el.dom.setAttribute('tabIndex', -1);
42879         this.el.addClass('x-hidden hide');
42880         
42881         
42882         
42883         if(Roo.isIE){ // fix IE 1px bogus margin
42884             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
42885         }
42886        
42887         
42888         this.frameId = Roo.id();
42889         
42890          
42891         
42892         var iframe = this.owner.wrap.createChild({
42893             tag: 'iframe',
42894             cls: 'form-control', // bootstrap..
42895             id: this.frameId,
42896             name: this.frameId,
42897             frameBorder : 'no',
42898             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
42899         }, this.el
42900         );
42901         
42902         
42903         this.iframe = iframe.dom;
42904
42905          this.assignDocWin();
42906         
42907         this.doc.designMode = 'on';
42908        
42909         this.doc.open();
42910         this.doc.write(this.getDocMarkup());
42911         this.doc.close();
42912
42913         
42914         var task = { // must defer to wait for browser to be ready
42915             run : function(){
42916                 //console.log("run task?" + this.doc.readyState);
42917                 this.assignDocWin();
42918                 if(this.doc.body || this.doc.readyState == 'complete'){
42919                     try {
42920                         this.doc.designMode="on";
42921                     } catch (e) {
42922                         return;
42923                     }
42924                     Roo.TaskMgr.stop(task);
42925                     this.initEditor.defer(10, this);
42926                 }
42927             },
42928             interval : 10,
42929             duration: 10000,
42930             scope: this
42931         };
42932         Roo.TaskMgr.start(task);
42933
42934     },
42935
42936     // private
42937     onResize : function(w, h)
42938     {
42939          Roo.log('resize: ' +w + ',' + h );
42940         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
42941         if(!this.iframe){
42942             return;
42943         }
42944         if(typeof w == 'number'){
42945             
42946             this.iframe.style.width = w + 'px';
42947         }
42948         if(typeof h == 'number'){
42949             
42950             this.iframe.style.height = h + 'px';
42951             if(this.doc){
42952                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
42953             }
42954         }
42955         
42956     },
42957
42958     /**
42959      * Toggles the editor between standard and source edit mode.
42960      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42961      */
42962     toggleSourceEdit : function(sourceEditMode){
42963         
42964         this.sourceEditMode = sourceEditMode === true;
42965         
42966         if(this.sourceEditMode){
42967  
42968             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
42969             
42970         }else{
42971             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
42972             //this.iframe.className = '';
42973             this.deferFocus();
42974         }
42975         //this.setSize(this.owner.wrap.getSize());
42976         //this.fireEvent('editmodechange', this, this.sourceEditMode);
42977     },
42978
42979     
42980   
42981
42982     /**
42983      * Protected method that will not generally be called directly. If you need/want
42984      * custom HTML cleanup, this is the method you should override.
42985      * @param {String} html The HTML to be cleaned
42986      * return {String} The cleaned HTML
42987      */
42988     cleanHtml : function(html){
42989         html = String(html);
42990         if(html.length > 5){
42991             if(Roo.isSafari){ // strip safari nonsense
42992                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
42993             }
42994         }
42995         if(html == '&nbsp;'){
42996             html = '';
42997         }
42998         return html;
42999     },
43000
43001     /**
43002      * HTML Editor -> Textarea
43003      * Protected method that will not generally be called directly. Syncs the contents
43004      * of the editor iframe with the textarea.
43005      */
43006     syncValue : function(){
43007         if(this.initialized){
43008             var bd = (this.doc.body || this.doc.documentElement);
43009             //this.cleanUpPaste(); -- this is done else where and causes havoc..
43010             var html = bd.innerHTML;
43011             if(Roo.isSafari){
43012                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43013                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43014                 if(m && m[1]){
43015                     html = '<div style="'+m[0]+'">' + html + '</div>';
43016                 }
43017             }
43018             html = this.cleanHtml(html);
43019             // fix up the special chars.. normaly like back quotes in word...
43020             // however we do not want to do this with chinese..
43021             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
43022                 var cc = b.charCodeAt();
43023                 if (
43024                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43025                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43026                     (cc >= 0xf900 && cc < 0xfb00 )
43027                 ) {
43028                         return b;
43029                 }
43030                 return "&#"+cc+";" 
43031             });
43032             if(this.owner.fireEvent('beforesync', this, html) !== false){
43033                 this.el.dom.value = html;
43034                 this.owner.fireEvent('sync', this, html);
43035             }
43036         }
43037     },
43038
43039     /**
43040      * Protected method that will not generally be called directly. Pushes the value of the textarea
43041      * into the iframe editor.
43042      */
43043     pushValue : function(){
43044         if(this.initialized){
43045             var v = this.el.dom.value.trim();
43046             
43047 //            if(v.length < 1){
43048 //                v = '&#160;';
43049 //            }
43050             
43051             if(this.owner.fireEvent('beforepush', this, v) !== false){
43052                 var d = (this.doc.body || this.doc.documentElement);
43053                 d.innerHTML = v;
43054                 this.cleanUpPaste();
43055                 this.el.dom.value = d.innerHTML;
43056                 this.owner.fireEvent('push', this, v);
43057             }
43058         }
43059     },
43060
43061     // private
43062     deferFocus : function(){
43063         this.focus.defer(10, this);
43064     },
43065
43066     // doc'ed in Field
43067     focus : function(){
43068         if(this.win && !this.sourceEditMode){
43069             this.win.focus();
43070         }else{
43071             this.el.focus();
43072         }
43073     },
43074     
43075     assignDocWin: function()
43076     {
43077         var iframe = this.iframe;
43078         
43079          if(Roo.isIE){
43080             this.doc = iframe.contentWindow.document;
43081             this.win = iframe.contentWindow;
43082         } else {
43083 //            if (!Roo.get(this.frameId)) {
43084 //                return;
43085 //            }
43086 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43087 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43088             
43089             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43090                 return;
43091             }
43092             
43093             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43094             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43095         }
43096     },
43097     
43098     // private
43099     initEditor : function(){
43100         //console.log("INIT EDITOR");
43101         this.assignDocWin();
43102         
43103         
43104         
43105         this.doc.designMode="on";
43106         this.doc.open();
43107         this.doc.write(this.getDocMarkup());
43108         this.doc.close();
43109         
43110         var dbody = (this.doc.body || this.doc.documentElement);
43111         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43112         // this copies styles from the containing element into thsi one..
43113         // not sure why we need all of this..
43114         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43115         
43116         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43117         //ss['background-attachment'] = 'fixed'; // w3c
43118         dbody.bgProperties = 'fixed'; // ie
43119         //Roo.DomHelper.applyStyles(dbody, ss);
43120         Roo.EventManager.on(this.doc, {
43121             //'mousedown': this.onEditorEvent,
43122             'mouseup': this.onEditorEvent,
43123             'dblclick': this.onEditorEvent,
43124             'click': this.onEditorEvent,
43125             'keyup': this.onEditorEvent,
43126             buffer:100,
43127             scope: this
43128         });
43129         if(Roo.isGecko){
43130             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43131         }
43132         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43133             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43134         }
43135         this.initialized = true;
43136
43137         this.owner.fireEvent('initialize', this);
43138         this.pushValue();
43139     },
43140
43141     // private
43142     onDestroy : function(){
43143         
43144         
43145         
43146         if(this.rendered){
43147             
43148             //for (var i =0; i < this.toolbars.length;i++) {
43149             //    // fixme - ask toolbars for heights?
43150             //    this.toolbars[i].onDestroy();
43151            // }
43152             
43153             //this.wrap.dom.innerHTML = '';
43154             //this.wrap.remove();
43155         }
43156     },
43157
43158     // private
43159     onFirstFocus : function(){
43160         
43161         this.assignDocWin();
43162         
43163         
43164         this.activated = true;
43165          
43166     
43167         if(Roo.isGecko){ // prevent silly gecko errors
43168             this.win.focus();
43169             var s = this.win.getSelection();
43170             if(!s.focusNode || s.focusNode.nodeType != 3){
43171                 var r = s.getRangeAt(0);
43172                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43173                 r.collapse(true);
43174                 this.deferFocus();
43175             }
43176             try{
43177                 this.execCmd('useCSS', true);
43178                 this.execCmd('styleWithCSS', false);
43179             }catch(e){}
43180         }
43181         this.owner.fireEvent('activate', this);
43182     },
43183
43184     // private
43185     adjustFont: function(btn){
43186         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
43187         //if(Roo.isSafari){ // safari
43188         //    adjust *= 2;
43189        // }
43190         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
43191         if(Roo.isSafari){ // safari
43192             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
43193             v =  (v < 10) ? 10 : v;
43194             v =  (v > 48) ? 48 : v;
43195             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
43196             
43197         }
43198         
43199         
43200         v = Math.max(1, v+adjust);
43201         
43202         this.execCmd('FontSize', v  );
43203     },
43204
43205     onEditorEvent : function(e)
43206     {
43207         this.owner.fireEvent('editorevent', this, e);
43208       //  this.updateToolbar();
43209         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
43210     },
43211
43212     insertTag : function(tg)
43213     {
43214         // could be a bit smarter... -> wrap the current selected tRoo..
43215         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
43216             
43217             range = this.createRange(this.getSelection());
43218             var wrappingNode = this.doc.createElement(tg.toLowerCase());
43219             wrappingNode.appendChild(range.extractContents());
43220             range.insertNode(wrappingNode);
43221
43222             return;
43223             
43224             
43225             
43226         }
43227         this.execCmd("formatblock",   tg);
43228         
43229     },
43230     
43231     insertText : function(txt)
43232     {
43233         
43234         
43235         var range = this.createRange();
43236         range.deleteContents();
43237                //alert(Sender.getAttribute('label'));
43238                
43239         range.insertNode(this.doc.createTextNode(txt));
43240     } ,
43241     
43242      
43243
43244     /**
43245      * Executes a Midas editor command on the editor document and performs necessary focus and
43246      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
43247      * @param {String} cmd The Midas command
43248      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43249      */
43250     relayCmd : function(cmd, value){
43251         this.win.focus();
43252         this.execCmd(cmd, value);
43253         this.owner.fireEvent('editorevent', this);
43254         //this.updateToolbar();
43255         this.owner.deferFocus();
43256     },
43257
43258     /**
43259      * Executes a Midas editor command directly on the editor document.
43260      * For visual commands, you should use {@link #relayCmd} instead.
43261      * <b>This should only be called after the editor is initialized.</b>
43262      * @param {String} cmd The Midas command
43263      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43264      */
43265     execCmd : function(cmd, value){
43266         this.doc.execCommand(cmd, false, value === undefined ? null : value);
43267         this.syncValue();
43268     },
43269  
43270  
43271    
43272     /**
43273      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
43274      * to insert tRoo.
43275      * @param {String} text | dom node.. 
43276      */
43277     insertAtCursor : function(text)
43278     {
43279         
43280         if(!this.activated){
43281             return;
43282         }
43283         /*
43284         if(Roo.isIE){
43285             this.win.focus();
43286             var r = this.doc.selection.createRange();
43287             if(r){
43288                 r.collapse(true);
43289                 r.pasteHTML(text);
43290                 this.syncValue();
43291                 this.deferFocus();
43292             
43293             }
43294             return;
43295         }
43296         */
43297         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
43298             this.win.focus();
43299             
43300             
43301             // from jquery ui (MIT licenced)
43302             var range, node;
43303             var win = this.win;
43304             
43305             if (win.getSelection && win.getSelection().getRangeAt) {
43306                 range = win.getSelection().getRangeAt(0);
43307                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
43308                 range.insertNode(node);
43309             } else if (win.document.selection && win.document.selection.createRange) {
43310                 // no firefox support
43311                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43312                 win.document.selection.createRange().pasteHTML(txt);
43313             } else {
43314                 // no firefox support
43315                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43316                 this.execCmd('InsertHTML', txt);
43317             } 
43318             
43319             this.syncValue();
43320             
43321             this.deferFocus();
43322         }
43323     },
43324  // private
43325     mozKeyPress : function(e){
43326         if(e.ctrlKey){
43327             var c = e.getCharCode(), cmd;
43328           
43329             if(c > 0){
43330                 c = String.fromCharCode(c).toLowerCase();
43331                 switch(c){
43332                     case 'b':
43333                         cmd = 'bold';
43334                         break;
43335                     case 'i':
43336                         cmd = 'italic';
43337                         break;
43338                     
43339                     case 'u':
43340                         cmd = 'underline';
43341                         break;
43342                     
43343                     case 'v':
43344                         this.cleanUpPaste.defer(100, this);
43345                         return;
43346                         
43347                 }
43348                 if(cmd){
43349                     this.win.focus();
43350                     this.execCmd(cmd);
43351                     this.deferFocus();
43352                     e.preventDefault();
43353                 }
43354                 
43355             }
43356         }
43357     },
43358
43359     // private
43360     fixKeys : function(){ // load time branching for fastest keydown performance
43361         if(Roo.isIE){
43362             return function(e){
43363                 var k = e.getKey(), r;
43364                 if(k == e.TAB){
43365                     e.stopEvent();
43366                     r = this.doc.selection.createRange();
43367                     if(r){
43368                         r.collapse(true);
43369                         r.pasteHTML('&#160;&#160;&#160;&#160;');
43370                         this.deferFocus();
43371                     }
43372                     return;
43373                 }
43374                 
43375                 if(k == e.ENTER){
43376                     r = this.doc.selection.createRange();
43377                     if(r){
43378                         var target = r.parentElement();
43379                         if(!target || target.tagName.toLowerCase() != 'li'){
43380                             e.stopEvent();
43381                             r.pasteHTML('<br />');
43382                             r.collapse(false);
43383                             r.select();
43384                         }
43385                     }
43386                 }
43387                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43388                     this.cleanUpPaste.defer(100, this);
43389                     return;
43390                 }
43391                 
43392                 
43393             };
43394         }else if(Roo.isOpera){
43395             return function(e){
43396                 var k = e.getKey();
43397                 if(k == e.TAB){
43398                     e.stopEvent();
43399                     this.win.focus();
43400                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
43401                     this.deferFocus();
43402                 }
43403                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43404                     this.cleanUpPaste.defer(100, this);
43405                     return;
43406                 }
43407                 
43408             };
43409         }else if(Roo.isSafari){
43410             return function(e){
43411                 var k = e.getKey();
43412                 
43413                 if(k == e.TAB){
43414                     e.stopEvent();
43415                     this.execCmd('InsertText','\t');
43416                     this.deferFocus();
43417                     return;
43418                 }
43419                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43420                     this.cleanUpPaste.defer(100, this);
43421                     return;
43422                 }
43423                 
43424              };
43425         }
43426     }(),
43427     
43428     getAllAncestors: function()
43429     {
43430         var p = this.getSelectedNode();
43431         var a = [];
43432         if (!p) {
43433             a.push(p); // push blank onto stack..
43434             p = this.getParentElement();
43435         }
43436         
43437         
43438         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
43439             a.push(p);
43440             p = p.parentNode;
43441         }
43442         a.push(this.doc.body);
43443         return a;
43444     },
43445     lastSel : false,
43446     lastSelNode : false,
43447     
43448     
43449     getSelection : function() 
43450     {
43451         this.assignDocWin();
43452         return Roo.isIE ? this.doc.selection : this.win.getSelection();
43453     },
43454     
43455     getSelectedNode: function() 
43456     {
43457         // this may only work on Gecko!!!
43458         
43459         // should we cache this!!!!
43460         
43461         
43462         
43463          
43464         var range = this.createRange(this.getSelection()).cloneRange();
43465         
43466         if (Roo.isIE) {
43467             var parent = range.parentElement();
43468             while (true) {
43469                 var testRange = range.duplicate();
43470                 testRange.moveToElementText(parent);
43471                 if (testRange.inRange(range)) {
43472                     break;
43473                 }
43474                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
43475                     break;
43476                 }
43477                 parent = parent.parentElement;
43478             }
43479             return parent;
43480         }
43481         
43482         // is ancestor a text element.
43483         var ac =  range.commonAncestorContainer;
43484         if (ac.nodeType == 3) {
43485             ac = ac.parentNode;
43486         }
43487         
43488         var ar = ac.childNodes;
43489          
43490         var nodes = [];
43491         var other_nodes = [];
43492         var has_other_nodes = false;
43493         for (var i=0;i<ar.length;i++) {
43494             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
43495                 continue;
43496             }
43497             // fullly contained node.
43498             
43499             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
43500                 nodes.push(ar[i]);
43501                 continue;
43502             }
43503             
43504             // probably selected..
43505             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
43506                 other_nodes.push(ar[i]);
43507                 continue;
43508             }
43509             // outer..
43510             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
43511                 continue;
43512             }
43513             
43514             
43515             has_other_nodes = true;
43516         }
43517         if (!nodes.length && other_nodes.length) {
43518             nodes= other_nodes;
43519         }
43520         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
43521             return false;
43522         }
43523         
43524         return nodes[0];
43525     },
43526     createRange: function(sel)
43527     {
43528         // this has strange effects when using with 
43529         // top toolbar - not sure if it's a great idea.
43530         //this.editor.contentWindow.focus();
43531         if (typeof sel != "undefined") {
43532             try {
43533                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
43534             } catch(e) {
43535                 return this.doc.createRange();
43536             }
43537         } else {
43538             return this.doc.createRange();
43539         }
43540     },
43541     getParentElement: function()
43542     {
43543         
43544         this.assignDocWin();
43545         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
43546         
43547         var range = this.createRange(sel);
43548          
43549         try {
43550             var p = range.commonAncestorContainer;
43551             while (p.nodeType == 3) { // text node
43552                 p = p.parentNode;
43553             }
43554             return p;
43555         } catch (e) {
43556             return null;
43557         }
43558     
43559     },
43560     /***
43561      *
43562      * Range intersection.. the hard stuff...
43563      *  '-1' = before
43564      *  '0' = hits..
43565      *  '1' = after.
43566      *         [ -- selected range --- ]
43567      *   [fail]                        [fail]
43568      *
43569      *    basically..
43570      *      if end is before start or  hits it. fail.
43571      *      if start is after end or hits it fail.
43572      *
43573      *   if either hits (but other is outside. - then it's not 
43574      *   
43575      *    
43576      **/
43577     
43578     
43579     // @see http://www.thismuchiknow.co.uk/?p=64.
43580     rangeIntersectsNode : function(range, node)
43581     {
43582         var nodeRange = node.ownerDocument.createRange();
43583         try {
43584             nodeRange.selectNode(node);
43585         } catch (e) {
43586             nodeRange.selectNodeContents(node);
43587         }
43588     
43589         var rangeStartRange = range.cloneRange();
43590         rangeStartRange.collapse(true);
43591     
43592         var rangeEndRange = range.cloneRange();
43593         rangeEndRange.collapse(false);
43594     
43595         var nodeStartRange = nodeRange.cloneRange();
43596         nodeStartRange.collapse(true);
43597     
43598         var nodeEndRange = nodeRange.cloneRange();
43599         nodeEndRange.collapse(false);
43600     
43601         return rangeStartRange.compareBoundaryPoints(
43602                  Range.START_TO_START, nodeEndRange) == -1 &&
43603                rangeEndRange.compareBoundaryPoints(
43604                  Range.START_TO_START, nodeStartRange) == 1;
43605         
43606          
43607     },
43608     rangeCompareNode : function(range, node)
43609     {
43610         var nodeRange = node.ownerDocument.createRange();
43611         try {
43612             nodeRange.selectNode(node);
43613         } catch (e) {
43614             nodeRange.selectNodeContents(node);
43615         }
43616         
43617         
43618         range.collapse(true);
43619     
43620         nodeRange.collapse(true);
43621      
43622         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
43623         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
43624          
43625         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
43626         
43627         var nodeIsBefore   =  ss == 1;
43628         var nodeIsAfter    = ee == -1;
43629         
43630         if (nodeIsBefore && nodeIsAfter) {
43631             return 0; // outer
43632         }
43633         if (!nodeIsBefore && nodeIsAfter) {
43634             return 1; //right trailed.
43635         }
43636         
43637         if (nodeIsBefore && !nodeIsAfter) {
43638             return 2;  // left trailed.
43639         }
43640         // fully contined.
43641         return 3;
43642     },
43643
43644     // private? - in a new class?
43645     cleanUpPaste :  function()
43646     {
43647         // cleans up the whole document..
43648         Roo.log('cleanuppaste');
43649         
43650         this.cleanUpChildren(this.doc.body);
43651         var clean = this.cleanWordChars(this.doc.body.innerHTML);
43652         if (clean != this.doc.body.innerHTML) {
43653             this.doc.body.innerHTML = clean;
43654         }
43655         
43656     },
43657     
43658     cleanWordChars : function(input) {// change the chars to hex code
43659         var he = Roo.HtmlEditorCore;
43660         
43661         var output = input;
43662         Roo.each(he.swapCodes, function(sw) { 
43663             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
43664             
43665             output = output.replace(swapper, sw[1]);
43666         });
43667         
43668         return output;
43669     },
43670     
43671     
43672     cleanUpChildren : function (n)
43673     {
43674         if (!n.childNodes.length) {
43675             return;
43676         }
43677         for (var i = n.childNodes.length-1; i > -1 ; i--) {
43678            this.cleanUpChild(n.childNodes[i]);
43679         }
43680     },
43681     
43682     
43683         
43684     
43685     cleanUpChild : function (node)
43686     {
43687         var ed = this;
43688         //console.log(node);
43689         if (node.nodeName == "#text") {
43690             // clean up silly Windows -- stuff?
43691             return; 
43692         }
43693         if (node.nodeName == "#comment") {
43694             node.parentNode.removeChild(node);
43695             // clean up silly Windows -- stuff?
43696             return; 
43697         }
43698         var lcname = node.tagName.toLowerCase();
43699         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
43700         // whitelist of tags..
43701         
43702         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
43703             // remove node.
43704             node.parentNode.removeChild(node);
43705             return;
43706             
43707         }
43708         
43709         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
43710         
43711         // remove <a name=....> as rendering on yahoo mailer is borked with this.
43712         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
43713         
43714         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
43715         //    remove_keep_children = true;
43716         //}
43717         
43718         if (remove_keep_children) {
43719             this.cleanUpChildren(node);
43720             // inserts everything just before this node...
43721             while (node.childNodes.length) {
43722                 var cn = node.childNodes[0];
43723                 node.removeChild(cn);
43724                 node.parentNode.insertBefore(cn, node);
43725             }
43726             node.parentNode.removeChild(node);
43727             return;
43728         }
43729         
43730         if (!node.attributes || !node.attributes.length) {
43731             this.cleanUpChildren(node);
43732             return;
43733         }
43734         
43735         function cleanAttr(n,v)
43736         {
43737             
43738             if (v.match(/^\./) || v.match(/^\//)) {
43739                 return;
43740             }
43741             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
43742                 return;
43743             }
43744             if (v.match(/^#/)) {
43745                 return;
43746             }
43747 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
43748             node.removeAttribute(n);
43749             
43750         }
43751         
43752         var cwhite = this.cwhite;
43753         var cblack = this.cblack;
43754             
43755         function cleanStyle(n,v)
43756         {
43757             if (v.match(/expression/)) { //XSS?? should we even bother..
43758                 node.removeAttribute(n);
43759                 return;
43760             }
43761             
43762             var parts = v.split(/;/);
43763             var clean = [];
43764             
43765             Roo.each(parts, function(p) {
43766                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
43767                 if (!p.length) {
43768                     return true;
43769                 }
43770                 var l = p.split(':').shift().replace(/\s+/g,'');
43771                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
43772                 
43773                 if ( cwhite.length && cblack.indexOf(l) > -1) {
43774 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43775                     //node.removeAttribute(n);
43776                     return true;
43777                 }
43778                 //Roo.log()
43779                 // only allow 'c whitelisted system attributes'
43780                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
43781 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43782                     //node.removeAttribute(n);
43783                     return true;
43784                 }
43785                 
43786                 
43787                  
43788                 
43789                 clean.push(p);
43790                 return true;
43791             });
43792             if (clean.length) { 
43793                 node.setAttribute(n, clean.join(';'));
43794             } else {
43795                 node.removeAttribute(n);
43796             }
43797             
43798         }
43799         
43800         
43801         for (var i = node.attributes.length-1; i > -1 ; i--) {
43802             var a = node.attributes[i];
43803             //console.log(a);
43804             
43805             if (a.name.toLowerCase().substr(0,2)=='on')  {
43806                 node.removeAttribute(a.name);
43807                 continue;
43808             }
43809             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
43810                 node.removeAttribute(a.name);
43811                 continue;
43812             }
43813             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
43814                 cleanAttr(a.name,a.value); // fixme..
43815                 continue;
43816             }
43817             if (a.name == 'style') {
43818                 cleanStyle(a.name,a.value);
43819                 continue;
43820             }
43821             /// clean up MS crap..
43822             // tecnically this should be a list of valid class'es..
43823             
43824             
43825             if (a.name == 'class') {
43826                 if (a.value.match(/^Mso/)) {
43827                     node.className = '';
43828                 }
43829                 
43830                 if (a.value.match(/^body$/)) {
43831                     node.className = '';
43832                 }
43833                 continue;
43834             }
43835             
43836             // style cleanup!?
43837             // class cleanup?
43838             
43839         }
43840         
43841         
43842         this.cleanUpChildren(node);
43843         
43844         
43845     },
43846     
43847     /**
43848      * Clean up MS wordisms...
43849      */
43850     cleanWord : function(node)
43851     {
43852         
43853         
43854         if (!node) {
43855             this.cleanWord(this.doc.body);
43856             return;
43857         }
43858         if (node.nodeName == "#text") {
43859             // clean up silly Windows -- stuff?
43860             return; 
43861         }
43862         if (node.nodeName == "#comment") {
43863             node.parentNode.removeChild(node);
43864             // clean up silly Windows -- stuff?
43865             return; 
43866         }
43867         
43868         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
43869             node.parentNode.removeChild(node);
43870             return;
43871         }
43872         
43873         // remove - but keep children..
43874         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
43875             while (node.childNodes.length) {
43876                 var cn = node.childNodes[0];
43877                 node.removeChild(cn);
43878                 node.parentNode.insertBefore(cn, node);
43879             }
43880             node.parentNode.removeChild(node);
43881             this.iterateChildren(node, this.cleanWord);
43882             return;
43883         }
43884         // clean styles
43885         if (node.className.length) {
43886             
43887             var cn = node.className.split(/\W+/);
43888             var cna = [];
43889             Roo.each(cn, function(cls) {
43890                 if (cls.match(/Mso[a-zA-Z]+/)) {
43891                     return;
43892                 }
43893                 cna.push(cls);
43894             });
43895             node.className = cna.length ? cna.join(' ') : '';
43896             if (!cna.length) {
43897                 node.removeAttribute("class");
43898             }
43899         }
43900         
43901         if (node.hasAttribute("lang")) {
43902             node.removeAttribute("lang");
43903         }
43904         
43905         if (node.hasAttribute("style")) {
43906             
43907             var styles = node.getAttribute("style").split(";");
43908             var nstyle = [];
43909             Roo.each(styles, function(s) {
43910                 if (!s.match(/:/)) {
43911                     return;
43912                 }
43913                 var kv = s.split(":");
43914                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
43915                     return;
43916                 }
43917                 // what ever is left... we allow.
43918                 nstyle.push(s);
43919             });
43920             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
43921             if (!nstyle.length) {
43922                 node.removeAttribute('style');
43923             }
43924         }
43925         this.iterateChildren(node, this.cleanWord);
43926         
43927         
43928         
43929     },
43930     /**
43931      * iterateChildren of a Node, calling fn each time, using this as the scole..
43932      * @param {DomNode} node node to iterate children of.
43933      * @param {Function} fn method of this class to call on each item.
43934      */
43935     iterateChildren : function(node, fn)
43936     {
43937         if (!node.childNodes.length) {
43938                 return;
43939         }
43940         for (var i = node.childNodes.length-1; i > -1 ; i--) {
43941            fn.call(this, node.childNodes[i])
43942         }
43943     },
43944     
43945     
43946     /**
43947      * cleanTableWidths.
43948      *
43949      * Quite often pasting from word etc.. results in tables with column and widths.
43950      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
43951      *
43952      */
43953     cleanTableWidths : function(node)
43954     {
43955          
43956          
43957         if (!node) {
43958             this.cleanTableWidths(this.doc.body);
43959             return;
43960         }
43961         
43962         // ignore list...
43963         if (node.nodeName == "#text" || node.nodeName == "#comment") {
43964             return; 
43965         }
43966         Roo.log(node.tagName);
43967         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
43968             this.iterateChildren(node, this.cleanTableWidths);
43969             return;
43970         }
43971         if (node.hasAttribute('width')) {
43972             node.removeAttribute('width');
43973         }
43974         
43975          
43976         if (node.hasAttribute("style")) {
43977             // pretty basic...
43978             
43979             var styles = node.getAttribute("style").split(";");
43980             var nstyle = [];
43981             Roo.each(styles, function(s) {
43982                 if (!s.match(/:/)) {
43983                     return;
43984                 }
43985                 var kv = s.split(":");
43986                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
43987                     return;
43988                 }
43989                 // what ever is left... we allow.
43990                 nstyle.push(s);
43991             });
43992             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
43993             if (!nstyle.length) {
43994                 node.removeAttribute('style');
43995             }
43996         }
43997         
43998         this.iterateChildren(node, this.cleanTableWidths);
43999         
44000         
44001     },
44002     
44003     
44004     
44005     
44006     domToHTML : function(currentElement, depth, nopadtext) {
44007         
44008         depth = depth || 0;
44009         nopadtext = nopadtext || false;
44010     
44011         if (!currentElement) {
44012             return this.domToHTML(this.doc.body);
44013         }
44014         
44015         //Roo.log(currentElement);
44016         var j;
44017         var allText = false;
44018         var nodeName = currentElement.nodeName;
44019         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
44020         
44021         if  (nodeName == '#text') {
44022             
44023             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
44024         }
44025         
44026         
44027         var ret = '';
44028         if (nodeName != 'BODY') {
44029              
44030             var i = 0;
44031             // Prints the node tagName, such as <A>, <IMG>, etc
44032             if (tagName) {
44033                 var attr = [];
44034                 for(i = 0; i < currentElement.attributes.length;i++) {
44035                     // quoting?
44036                     var aname = currentElement.attributes.item(i).name;
44037                     if (!currentElement.attributes.item(i).value.length) {
44038                         continue;
44039                     }
44040                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44041                 }
44042                 
44043                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44044             } 
44045             else {
44046                 
44047                 // eack
44048             }
44049         } else {
44050             tagName = false;
44051         }
44052         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44053             return ret;
44054         }
44055         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44056             nopadtext = true;
44057         }
44058         
44059         
44060         // Traverse the tree
44061         i = 0;
44062         var currentElementChild = currentElement.childNodes.item(i);
44063         var allText = true;
44064         var innerHTML  = '';
44065         lastnode = '';
44066         while (currentElementChild) {
44067             // Formatting code (indent the tree so it looks nice on the screen)
44068             var nopad = nopadtext;
44069             if (lastnode == 'SPAN') {
44070                 nopad  = true;
44071             }
44072             // text
44073             if  (currentElementChild.nodeName == '#text') {
44074                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44075                 toadd = nopadtext ? toadd : toadd.trim();
44076                 if (!nopad && toadd.length > 80) {
44077                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44078                 }
44079                 innerHTML  += toadd;
44080                 
44081                 i++;
44082                 currentElementChild = currentElement.childNodes.item(i);
44083                 lastNode = '';
44084                 continue;
44085             }
44086             allText = false;
44087             
44088             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44089                 
44090             // Recursively traverse the tree structure of the child node
44091             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44092             lastnode = currentElementChild.nodeName;
44093             i++;
44094             currentElementChild=currentElement.childNodes.item(i);
44095         }
44096         
44097         ret += innerHTML;
44098         
44099         if (!allText) {
44100                 // The remaining code is mostly for formatting the tree
44101             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44102         }
44103         
44104         
44105         if (tagName) {
44106             ret+= "</"+tagName+">";
44107         }
44108         return ret;
44109         
44110     },
44111         
44112     applyBlacklists : function()
44113     {
44114         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44115         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44116         
44117         this.white = [];
44118         this.black = [];
44119         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44120             if (b.indexOf(tag) > -1) {
44121                 return;
44122             }
44123             this.white.push(tag);
44124             
44125         }, this);
44126         
44127         Roo.each(w, function(tag) {
44128             if (b.indexOf(tag) > -1) {
44129                 return;
44130             }
44131             if (this.white.indexOf(tag) > -1) {
44132                 return;
44133             }
44134             this.white.push(tag);
44135             
44136         }, this);
44137         
44138         
44139         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44140             if (w.indexOf(tag) > -1) {
44141                 return;
44142             }
44143             this.black.push(tag);
44144             
44145         }, this);
44146         
44147         Roo.each(b, function(tag) {
44148             if (w.indexOf(tag) > -1) {
44149                 return;
44150             }
44151             if (this.black.indexOf(tag) > -1) {
44152                 return;
44153             }
44154             this.black.push(tag);
44155             
44156         }, this);
44157         
44158         
44159         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
44160         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
44161         
44162         this.cwhite = [];
44163         this.cblack = [];
44164         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
44165             if (b.indexOf(tag) > -1) {
44166                 return;
44167             }
44168             this.cwhite.push(tag);
44169             
44170         }, this);
44171         
44172         Roo.each(w, function(tag) {
44173             if (b.indexOf(tag) > -1) {
44174                 return;
44175             }
44176             if (this.cwhite.indexOf(tag) > -1) {
44177                 return;
44178             }
44179             this.cwhite.push(tag);
44180             
44181         }, this);
44182         
44183         
44184         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
44185             if (w.indexOf(tag) > -1) {
44186                 return;
44187             }
44188             this.cblack.push(tag);
44189             
44190         }, this);
44191         
44192         Roo.each(b, function(tag) {
44193             if (w.indexOf(tag) > -1) {
44194                 return;
44195             }
44196             if (this.cblack.indexOf(tag) > -1) {
44197                 return;
44198             }
44199             this.cblack.push(tag);
44200             
44201         }, this);
44202     },
44203     
44204     setStylesheets : function(stylesheets)
44205     {
44206         if(typeof(stylesheets) == 'string'){
44207             Roo.get(this.iframe.contentDocument.head).createChild({
44208                 tag : 'link',
44209                 rel : 'stylesheet',
44210                 type : 'text/css',
44211                 href : stylesheets
44212             });
44213             
44214             return;
44215         }
44216         var _this = this;
44217      
44218         Roo.each(stylesheets, function(s) {
44219             if(!s.length){
44220                 return;
44221             }
44222             
44223             Roo.get(_this.iframe.contentDocument.head).createChild({
44224                 tag : 'link',
44225                 rel : 'stylesheet',
44226                 type : 'text/css',
44227                 href : s
44228             });
44229         });
44230
44231         
44232     },
44233     
44234     removeStylesheets : function()
44235     {
44236         var _this = this;
44237         
44238         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
44239             s.remove();
44240         });
44241     }
44242     
44243     // hide stuff that is not compatible
44244     /**
44245      * @event blur
44246      * @hide
44247      */
44248     /**
44249      * @event change
44250      * @hide
44251      */
44252     /**
44253      * @event focus
44254      * @hide
44255      */
44256     /**
44257      * @event specialkey
44258      * @hide
44259      */
44260     /**
44261      * @cfg {String} fieldClass @hide
44262      */
44263     /**
44264      * @cfg {String} focusClass @hide
44265      */
44266     /**
44267      * @cfg {String} autoCreate @hide
44268      */
44269     /**
44270      * @cfg {String} inputType @hide
44271      */
44272     /**
44273      * @cfg {String} invalidClass @hide
44274      */
44275     /**
44276      * @cfg {String} invalidText @hide
44277      */
44278     /**
44279      * @cfg {String} msgFx @hide
44280      */
44281     /**
44282      * @cfg {String} validateOnBlur @hide
44283      */
44284 });
44285
44286 Roo.HtmlEditorCore.white = [
44287         'area', 'br', 'img', 'input', 'hr', 'wbr',
44288         
44289        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
44290        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
44291        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
44292        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
44293        'table',   'ul',         'xmp', 
44294        
44295        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
44296       'thead',   'tr', 
44297      
44298       'dir', 'menu', 'ol', 'ul', 'dl',
44299        
44300       'embed',  'object'
44301 ];
44302
44303
44304 Roo.HtmlEditorCore.black = [
44305     //    'embed',  'object', // enable - backend responsiblity to clean thiese
44306         'applet', // 
44307         'base',   'basefont', 'bgsound', 'blink',  'body', 
44308         'frame',  'frameset', 'head',    'html',   'ilayer', 
44309         'iframe', 'layer',  'link',     'meta',    'object',   
44310         'script', 'style' ,'title',  'xml' // clean later..
44311 ];
44312 Roo.HtmlEditorCore.clean = [
44313     'script', 'style', 'title', 'xml'
44314 ];
44315 Roo.HtmlEditorCore.remove = [
44316     'font'
44317 ];
44318 // attributes..
44319
44320 Roo.HtmlEditorCore.ablack = [
44321     'on'
44322 ];
44323     
44324 Roo.HtmlEditorCore.aclean = [ 
44325     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
44326 ];
44327
44328 // protocols..
44329 Roo.HtmlEditorCore.pwhite= [
44330         'http',  'https',  'mailto'
44331 ];
44332
44333 // white listed style attributes.
44334 Roo.HtmlEditorCore.cwhite= [
44335       //  'text-align', /// default is to allow most things..
44336       
44337          
44338 //        'font-size'//??
44339 ];
44340
44341 // black listed style attributes.
44342 Roo.HtmlEditorCore.cblack= [
44343       //  'font-size' -- this can be set by the project 
44344 ];
44345
44346
44347 Roo.HtmlEditorCore.swapCodes   =[ 
44348     [    8211, "--" ], 
44349     [    8212, "--" ], 
44350     [    8216,  "'" ],  
44351     [    8217, "'" ],  
44352     [    8220, '"' ],  
44353     [    8221, '"' ],  
44354     [    8226, "*" ],  
44355     [    8230, "..." ]
44356 ]; 
44357
44358     //<script type="text/javascript">
44359
44360 /*
44361  * Ext JS Library 1.1.1
44362  * Copyright(c) 2006-2007, Ext JS, LLC.
44363  * Licence LGPL
44364  * 
44365  */
44366  
44367  
44368 Roo.form.HtmlEditor = function(config){
44369     
44370     
44371     
44372     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
44373     
44374     if (!this.toolbars) {
44375         this.toolbars = [];
44376     }
44377     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
44378     
44379     
44380 };
44381
44382 /**
44383  * @class Roo.form.HtmlEditor
44384  * @extends Roo.form.Field
44385  * Provides a lightweight HTML Editor component.
44386  *
44387  * This has been tested on Fireforx / Chrome.. IE may not be so great..
44388  * 
44389  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
44390  * supported by this editor.</b><br/><br/>
44391  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
44392  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
44393  */
44394 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
44395     /**
44396      * @cfg {Boolean} clearUp
44397      */
44398     clearUp : true,
44399       /**
44400      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
44401      */
44402     toolbars : false,
44403    
44404      /**
44405      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
44406      *                        Roo.resizable.
44407      */
44408     resizable : false,
44409      /**
44410      * @cfg {Number} height (in pixels)
44411      */   
44412     height: 300,
44413    /**
44414      * @cfg {Number} width (in pixels)
44415      */   
44416     width: 500,
44417     
44418     /**
44419      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
44420      * 
44421      */
44422     stylesheets: false,
44423     
44424     
44425      /**
44426      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
44427      * 
44428      */
44429     cblack: false,
44430     /**
44431      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
44432      * 
44433      */
44434     cwhite: false,
44435     
44436      /**
44437      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
44438      * 
44439      */
44440     black: false,
44441     /**
44442      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
44443      * 
44444      */
44445     white: false,
44446     
44447     // id of frame..
44448     frameId: false,
44449     
44450     // private properties
44451     validationEvent : false,
44452     deferHeight: true,
44453     initialized : false,
44454     activated : false,
44455     
44456     onFocus : Roo.emptyFn,
44457     iframePad:3,
44458     hideMode:'offsets',
44459     
44460     actionMode : 'container', // defaults to hiding it...
44461     
44462     defaultAutoCreate : { // modified by initCompnoent..
44463         tag: "textarea",
44464         style:"width:500px;height:300px;",
44465         autocomplete: "new-password"
44466     },
44467
44468     // private
44469     initComponent : function(){
44470         this.addEvents({
44471             /**
44472              * @event initialize
44473              * Fires when the editor is fully initialized (including the iframe)
44474              * @param {HtmlEditor} this
44475              */
44476             initialize: true,
44477             /**
44478              * @event activate
44479              * Fires when the editor is first receives the focus. Any insertion must wait
44480              * until after this event.
44481              * @param {HtmlEditor} this
44482              */
44483             activate: true,
44484              /**
44485              * @event beforesync
44486              * Fires before the textarea is updated with content from the editor iframe. Return false
44487              * to cancel the sync.
44488              * @param {HtmlEditor} this
44489              * @param {String} html
44490              */
44491             beforesync: true,
44492              /**
44493              * @event beforepush
44494              * Fires before the iframe editor is updated with content from the textarea. Return false
44495              * to cancel the push.
44496              * @param {HtmlEditor} this
44497              * @param {String} html
44498              */
44499             beforepush: true,
44500              /**
44501              * @event sync
44502              * Fires when the textarea is updated with content from the editor iframe.
44503              * @param {HtmlEditor} this
44504              * @param {String} html
44505              */
44506             sync: true,
44507              /**
44508              * @event push
44509              * Fires when the iframe editor is updated with content from the textarea.
44510              * @param {HtmlEditor} this
44511              * @param {String} html
44512              */
44513             push: true,
44514              /**
44515              * @event editmodechange
44516              * Fires when the editor switches edit modes
44517              * @param {HtmlEditor} this
44518              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
44519              */
44520             editmodechange: true,
44521             /**
44522              * @event editorevent
44523              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
44524              * @param {HtmlEditor} this
44525              */
44526             editorevent: true,
44527             /**
44528              * @event firstfocus
44529              * Fires when on first focus - needed by toolbars..
44530              * @param {HtmlEditor} this
44531              */
44532             firstfocus: true,
44533             /**
44534              * @event autosave
44535              * Auto save the htmlEditor value as a file into Events
44536              * @param {HtmlEditor} this
44537              */
44538             autosave: true,
44539             /**
44540              * @event savedpreview
44541              * preview the saved version of htmlEditor
44542              * @param {HtmlEditor} this
44543              */
44544             savedpreview: true,
44545             
44546             /**
44547             * @event stylesheetsclick
44548             * Fires when press the Sytlesheets button
44549             * @param {Roo.HtmlEditorCore} this
44550             */
44551             stylesheetsclick: true
44552         });
44553         this.defaultAutoCreate =  {
44554             tag: "textarea",
44555             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
44556             autocomplete: "new-password"
44557         };
44558     },
44559
44560     /**
44561      * Protected method that will not generally be called directly. It
44562      * is called when the editor creates its toolbar. Override this method if you need to
44563      * add custom toolbar buttons.
44564      * @param {HtmlEditor} editor
44565      */
44566     createToolbar : function(editor){
44567         Roo.log("create toolbars");
44568         if (!editor.toolbars || !editor.toolbars.length) {
44569             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
44570         }
44571         
44572         for (var i =0 ; i < editor.toolbars.length;i++) {
44573             editor.toolbars[i] = Roo.factory(
44574                     typeof(editor.toolbars[i]) == 'string' ?
44575                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
44576                 Roo.form.HtmlEditor);
44577             editor.toolbars[i].init(editor);
44578         }
44579          
44580         
44581     },
44582
44583      
44584     // private
44585     onRender : function(ct, position)
44586     {
44587         var _t = this;
44588         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
44589         
44590         this.wrap = this.el.wrap({
44591             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
44592         });
44593         
44594         this.editorcore.onRender(ct, position);
44595          
44596         if (this.resizable) {
44597             this.resizeEl = new Roo.Resizable(this.wrap, {
44598                 pinned : true,
44599                 wrap: true,
44600                 dynamic : true,
44601                 minHeight : this.height,
44602                 height: this.height,
44603                 handles : this.resizable,
44604                 width: this.width,
44605                 listeners : {
44606                     resize : function(r, w, h) {
44607                         _t.onResize(w,h); // -something
44608                     }
44609                 }
44610             });
44611             
44612         }
44613         this.createToolbar(this);
44614        
44615         
44616         if(!this.width){
44617             this.setSize(this.wrap.getSize());
44618         }
44619         if (this.resizeEl) {
44620             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
44621             // should trigger onReize..
44622         }
44623         
44624         this.keyNav = new Roo.KeyNav(this.el, {
44625             
44626             "tab" : function(e){
44627                 e.preventDefault();
44628                 
44629                 var value = this.getValue();
44630                 
44631                 var start = this.el.dom.selectionStart;
44632                 var end = this.el.dom.selectionEnd;
44633                 
44634                 if(!e.shiftKey){
44635                     
44636                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
44637                     this.el.dom.setSelectionRange(end + 1, end + 1);
44638                     return;
44639                 }
44640                 
44641                 var f = value.substring(0, start).split("\t");
44642                 
44643                 if(f.pop().length != 0){
44644                     return;
44645                 }
44646                 
44647                 this.setValue(f.join("\t") + value.substring(end));
44648                 this.el.dom.setSelectionRange(start - 1, start - 1);
44649                 
44650             },
44651             
44652             "home" : function(e){
44653                 e.preventDefault();
44654                 
44655                 var curr = this.el.dom.selectionStart;
44656                 var lines = this.getValue().split("\n");
44657                 
44658                 if(!lines.length){
44659                     return;
44660                 }
44661                 
44662                 if(e.ctrlKey){
44663                     this.el.dom.setSelectionRange(0, 0);
44664                     return;
44665                 }
44666                 
44667                 var pos = 0;
44668                 
44669                 for (var i = 0; i < lines.length;i++) {
44670                     pos += lines[i].length;
44671                     
44672                     if(i != 0){
44673                         pos += 1;
44674                     }
44675                     
44676                     if(pos < curr){
44677                         continue;
44678                     }
44679                     
44680                     pos -= lines[i].length;
44681                     
44682                     break;
44683                 }
44684                 
44685                 if(!e.shiftKey){
44686                     this.el.dom.setSelectionRange(pos, pos);
44687                     return;
44688                 }
44689                 
44690                 this.el.dom.selectionStart = pos;
44691                 this.el.dom.selectionEnd = curr;
44692             },
44693             
44694             "end" : function(e){
44695                 e.preventDefault();
44696                 
44697                 var curr = this.el.dom.selectionStart;
44698                 var lines = this.getValue().split("\n");
44699                 
44700                 if(!lines.length){
44701                     return;
44702                 }
44703                 
44704                 if(e.ctrlKey){
44705                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
44706                     return;
44707                 }
44708                 
44709                 var pos = 0;
44710                 
44711                 for (var i = 0; i < lines.length;i++) {
44712                     
44713                     pos += lines[i].length;
44714                     
44715                     if(i != 0){
44716                         pos += 1;
44717                     }
44718                     
44719                     if(pos < curr){
44720                         continue;
44721                     }
44722                     
44723                     break;
44724                 }
44725                 
44726                 if(!e.shiftKey){
44727                     this.el.dom.setSelectionRange(pos, pos);
44728                     return;
44729                 }
44730                 
44731                 this.el.dom.selectionStart = curr;
44732                 this.el.dom.selectionEnd = pos;
44733             },
44734
44735             scope : this,
44736
44737             doRelay : function(foo, bar, hname){
44738                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44739             },
44740
44741             forceKeyDown: true
44742         });
44743         
44744 //        if(this.autosave && this.w){
44745 //            this.autoSaveFn = setInterval(this.autosave, 1000);
44746 //        }
44747     },
44748
44749     // private
44750     onResize : function(w, h)
44751     {
44752         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
44753         var ew = false;
44754         var eh = false;
44755         
44756         if(this.el ){
44757             if(typeof w == 'number'){
44758                 var aw = w - this.wrap.getFrameWidth('lr');
44759                 this.el.setWidth(this.adjustWidth('textarea', aw));
44760                 ew = aw;
44761             }
44762             if(typeof h == 'number'){
44763                 var tbh = 0;
44764                 for (var i =0; i < this.toolbars.length;i++) {
44765                     // fixme - ask toolbars for heights?
44766                     tbh += this.toolbars[i].tb.el.getHeight();
44767                     if (this.toolbars[i].footer) {
44768                         tbh += this.toolbars[i].footer.el.getHeight();
44769                     }
44770                 }
44771                 
44772                 
44773                 
44774                 
44775                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
44776                 ah -= 5; // knock a few pixes off for look..
44777 //                Roo.log(ah);
44778                 this.el.setHeight(this.adjustWidth('textarea', ah));
44779                 var eh = ah;
44780             }
44781         }
44782         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
44783         this.editorcore.onResize(ew,eh);
44784         
44785     },
44786
44787     /**
44788      * Toggles the editor between standard and source edit mode.
44789      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
44790      */
44791     toggleSourceEdit : function(sourceEditMode)
44792     {
44793         this.editorcore.toggleSourceEdit(sourceEditMode);
44794         
44795         if(this.editorcore.sourceEditMode){
44796             Roo.log('editor - showing textarea');
44797             
44798 //            Roo.log('in');
44799 //            Roo.log(this.syncValue());
44800             this.editorcore.syncValue();
44801             this.el.removeClass('x-hidden');
44802             this.el.dom.removeAttribute('tabIndex');
44803             this.el.focus();
44804             
44805             for (var i = 0; i < this.toolbars.length; i++) {
44806                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44807                     this.toolbars[i].tb.hide();
44808                     this.toolbars[i].footer.hide();
44809                 }
44810             }
44811             
44812         }else{
44813             Roo.log('editor - hiding textarea');
44814 //            Roo.log('out')
44815 //            Roo.log(this.pushValue()); 
44816             this.editorcore.pushValue();
44817             
44818             this.el.addClass('x-hidden');
44819             this.el.dom.setAttribute('tabIndex', -1);
44820             
44821             for (var i = 0; i < this.toolbars.length; i++) {
44822                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44823                     this.toolbars[i].tb.show();
44824                     this.toolbars[i].footer.show();
44825                 }
44826             }
44827             
44828             //this.deferFocus();
44829         }
44830         
44831         this.setSize(this.wrap.getSize());
44832         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
44833         
44834         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
44835     },
44836  
44837     // private (for BoxComponent)
44838     adjustSize : Roo.BoxComponent.prototype.adjustSize,
44839
44840     // private (for BoxComponent)
44841     getResizeEl : function(){
44842         return this.wrap;
44843     },
44844
44845     // private (for BoxComponent)
44846     getPositionEl : function(){
44847         return this.wrap;
44848     },
44849
44850     // private
44851     initEvents : function(){
44852         this.originalValue = this.getValue();
44853     },
44854
44855     /**
44856      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44857      * @method
44858      */
44859     markInvalid : Roo.emptyFn,
44860     /**
44861      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44862      * @method
44863      */
44864     clearInvalid : Roo.emptyFn,
44865
44866     setValue : function(v){
44867         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
44868         this.editorcore.pushValue();
44869     },
44870
44871      
44872     // private
44873     deferFocus : function(){
44874         this.focus.defer(10, this);
44875     },
44876
44877     // doc'ed in Field
44878     focus : function(){
44879         this.editorcore.focus();
44880         
44881     },
44882       
44883
44884     // private
44885     onDestroy : function(){
44886         
44887         
44888         
44889         if(this.rendered){
44890             
44891             for (var i =0; i < this.toolbars.length;i++) {
44892                 // fixme - ask toolbars for heights?
44893                 this.toolbars[i].onDestroy();
44894             }
44895             
44896             this.wrap.dom.innerHTML = '';
44897             this.wrap.remove();
44898         }
44899     },
44900
44901     // private
44902     onFirstFocus : function(){
44903         //Roo.log("onFirstFocus");
44904         this.editorcore.onFirstFocus();
44905          for (var i =0; i < this.toolbars.length;i++) {
44906             this.toolbars[i].onFirstFocus();
44907         }
44908         
44909     },
44910     
44911     // private
44912     syncValue : function()
44913     {
44914         this.editorcore.syncValue();
44915     },
44916     
44917     pushValue : function()
44918     {
44919         this.editorcore.pushValue();
44920     },
44921     
44922     setStylesheets : function(stylesheets)
44923     {
44924         this.editorcore.setStylesheets(stylesheets);
44925     },
44926     
44927     removeStylesheets : function()
44928     {
44929         this.editorcore.removeStylesheets();
44930     }
44931      
44932     
44933     // hide stuff that is not compatible
44934     /**
44935      * @event blur
44936      * @hide
44937      */
44938     /**
44939      * @event change
44940      * @hide
44941      */
44942     /**
44943      * @event focus
44944      * @hide
44945      */
44946     /**
44947      * @event specialkey
44948      * @hide
44949      */
44950     /**
44951      * @cfg {String} fieldClass @hide
44952      */
44953     /**
44954      * @cfg {String} focusClass @hide
44955      */
44956     /**
44957      * @cfg {String} autoCreate @hide
44958      */
44959     /**
44960      * @cfg {String} inputType @hide
44961      */
44962     /**
44963      * @cfg {String} invalidClass @hide
44964      */
44965     /**
44966      * @cfg {String} invalidText @hide
44967      */
44968     /**
44969      * @cfg {String} msgFx @hide
44970      */
44971     /**
44972      * @cfg {String} validateOnBlur @hide
44973      */
44974 });
44975  
44976     // <script type="text/javascript">
44977 /*
44978  * Based on
44979  * Ext JS Library 1.1.1
44980  * Copyright(c) 2006-2007, Ext JS, LLC.
44981  *  
44982  
44983  */
44984
44985 /**
44986  * @class Roo.form.HtmlEditorToolbar1
44987  * Basic Toolbar
44988  * 
44989  * Usage:
44990  *
44991  new Roo.form.HtmlEditor({
44992     ....
44993     toolbars : [
44994         new Roo.form.HtmlEditorToolbar1({
44995             disable : { fonts: 1 , format: 1, ..., ... , ...],
44996             btns : [ .... ]
44997         })
44998     }
44999      
45000  * 
45001  * @cfg {Object} disable List of elements to disable..
45002  * @cfg {Array} btns List of additional buttons.
45003  * 
45004  * 
45005  * NEEDS Extra CSS? 
45006  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
45007  */
45008  
45009 Roo.form.HtmlEditor.ToolbarStandard = function(config)
45010 {
45011     
45012     Roo.apply(this, config);
45013     
45014     // default disabled, based on 'good practice'..
45015     this.disable = this.disable || {};
45016     Roo.applyIf(this.disable, {
45017         fontSize : true,
45018         colors : true,
45019         specialElements : true
45020     });
45021     
45022     
45023     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45024     // dont call parent... till later.
45025 }
45026
45027 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
45028     
45029     tb: false,
45030     
45031     rendered: false,
45032     
45033     editor : false,
45034     editorcore : false,
45035     /**
45036      * @cfg {Object} disable  List of toolbar elements to disable
45037          
45038      */
45039     disable : false,
45040     
45041     
45042      /**
45043      * @cfg {String} createLinkText The default text for the create link prompt
45044      */
45045     createLinkText : 'Please enter the URL for the link:',
45046     /**
45047      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45048      */
45049     defaultLinkValue : 'http:/'+'/',
45050    
45051     
45052       /**
45053      * @cfg {Array} fontFamilies An array of available font families
45054      */
45055     fontFamilies : [
45056         'Arial',
45057         'Courier New',
45058         'Tahoma',
45059         'Times New Roman',
45060         'Verdana'
45061     ],
45062     
45063     specialChars : [
45064            "&#169;",
45065           "&#174;",     
45066           "&#8482;",    
45067           "&#163;" ,    
45068          // "&#8212;",    
45069           "&#8230;",    
45070           "&#247;" ,    
45071         //  "&#225;" ,     ?? a acute?
45072            "&#8364;"    , //Euro
45073        //   "&#8220;"    ,
45074         //  "&#8221;"    ,
45075         //  "&#8226;"    ,
45076           "&#176;"  //   , // degrees
45077
45078          // "&#233;"     , // e ecute
45079          // "&#250;"     , // u ecute?
45080     ],
45081     
45082     specialElements : [
45083         {
45084             text: "Insert Table",
45085             xtype: 'MenuItem',
45086             xns : Roo.Menu,
45087             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45088                 
45089         },
45090         {    
45091             text: "Insert Image",
45092             xtype: 'MenuItem',
45093             xns : Roo.Menu,
45094             ihtml : '<img src="about:blank"/>'
45095             
45096         }
45097         
45098          
45099     ],
45100     
45101     
45102     inputElements : [ 
45103             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45104             "input:submit", "input:button", "select", "textarea", "label" ],
45105     formats : [
45106         ["p"] ,  
45107         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45108         ["pre"],[ "code"], 
45109         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45110         ['div'],['span']
45111     ],
45112     
45113     cleanStyles : [
45114         "font-size"
45115     ],
45116      /**
45117      * @cfg {String} defaultFont default font to use.
45118      */
45119     defaultFont: 'tahoma',
45120    
45121     fontSelect : false,
45122     
45123     
45124     formatCombo : false,
45125     
45126     init : function(editor)
45127     {
45128         this.editor = editor;
45129         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45130         var editorcore = this.editorcore;
45131         
45132         var _t = this;
45133         
45134         var fid = editorcore.frameId;
45135         var etb = this;
45136         function btn(id, toggle, handler){
45137             var xid = fid + '-'+ id ;
45138             return {
45139                 id : xid,
45140                 cmd : id,
45141                 cls : 'x-btn-icon x-edit-'+id,
45142                 enableToggle:toggle !== false,
45143                 scope: _t, // was editor...
45144                 handler:handler||_t.relayBtnCmd,
45145                 clickEvent:'mousedown',
45146                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45147                 tabIndex:-1
45148             };
45149         }
45150         
45151         
45152         
45153         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45154         this.tb = tb;
45155          // stop form submits
45156         tb.el.on('click', function(e){
45157             e.preventDefault(); // what does this do?
45158         });
45159
45160         if(!this.disable.font) { // && !Roo.isSafari){
45161             /* why no safari for fonts 
45162             editor.fontSelect = tb.el.createChild({
45163                 tag:'select',
45164                 tabIndex: -1,
45165                 cls:'x-font-select',
45166                 html: this.createFontOptions()
45167             });
45168             
45169             editor.fontSelect.on('change', function(){
45170                 var font = editor.fontSelect.dom.value;
45171                 editor.relayCmd('fontname', font);
45172                 editor.deferFocus();
45173             }, editor);
45174             
45175             tb.add(
45176                 editor.fontSelect.dom,
45177                 '-'
45178             );
45179             */
45180             
45181         };
45182         if(!this.disable.formats){
45183             this.formatCombo = new Roo.form.ComboBox({
45184                 store: new Roo.data.SimpleStore({
45185                     id : 'tag',
45186                     fields: ['tag'],
45187                     data : this.formats // from states.js
45188                 }),
45189                 blockFocus : true,
45190                 name : '',
45191                 //autoCreate : {tag: "div",  size: "20"},
45192                 displayField:'tag',
45193                 typeAhead: false,
45194                 mode: 'local',
45195                 editable : false,
45196                 triggerAction: 'all',
45197                 emptyText:'Add tag',
45198                 selectOnFocus:true,
45199                 width:135,
45200                 listeners : {
45201                     'select': function(c, r, i) {
45202                         editorcore.insertTag(r.get('tag'));
45203                         editor.focus();
45204                     }
45205                 }
45206
45207             });
45208             tb.addField(this.formatCombo);
45209             
45210         }
45211         
45212         if(!this.disable.format){
45213             tb.add(
45214                 btn('bold'),
45215                 btn('italic'),
45216                 btn('underline'),
45217                 btn('strikethrough')
45218             );
45219         };
45220         if(!this.disable.fontSize){
45221             tb.add(
45222                 '-',
45223                 
45224                 
45225                 btn('increasefontsize', false, editorcore.adjustFont),
45226                 btn('decreasefontsize', false, editorcore.adjustFont)
45227             );
45228         };
45229         
45230         
45231         if(!this.disable.colors){
45232             tb.add(
45233                 '-', {
45234                     id:editorcore.frameId +'-forecolor',
45235                     cls:'x-btn-icon x-edit-forecolor',
45236                     clickEvent:'mousedown',
45237                     tooltip: this.buttonTips['forecolor'] || undefined,
45238                     tabIndex:-1,
45239                     menu : new Roo.menu.ColorMenu({
45240                         allowReselect: true,
45241                         focus: Roo.emptyFn,
45242                         value:'000000',
45243                         plain:true,
45244                         selectHandler: function(cp, color){
45245                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
45246                             editor.deferFocus();
45247                         },
45248                         scope: editorcore,
45249                         clickEvent:'mousedown'
45250                     })
45251                 }, {
45252                     id:editorcore.frameId +'backcolor',
45253                     cls:'x-btn-icon x-edit-backcolor',
45254                     clickEvent:'mousedown',
45255                     tooltip: this.buttonTips['backcolor'] || undefined,
45256                     tabIndex:-1,
45257                     menu : new Roo.menu.ColorMenu({
45258                         focus: Roo.emptyFn,
45259                         value:'FFFFFF',
45260                         plain:true,
45261                         allowReselect: true,
45262                         selectHandler: function(cp, color){
45263                             if(Roo.isGecko){
45264                                 editorcore.execCmd('useCSS', false);
45265                                 editorcore.execCmd('hilitecolor', color);
45266                                 editorcore.execCmd('useCSS', true);
45267                                 editor.deferFocus();
45268                             }else{
45269                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
45270                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
45271                                 editor.deferFocus();
45272                             }
45273                         },
45274                         scope:editorcore,
45275                         clickEvent:'mousedown'
45276                     })
45277                 }
45278             );
45279         };
45280         // now add all the items...
45281         
45282
45283         if(!this.disable.alignments){
45284             tb.add(
45285                 '-',
45286                 btn('justifyleft'),
45287                 btn('justifycenter'),
45288                 btn('justifyright')
45289             );
45290         };
45291
45292         //if(!Roo.isSafari){
45293             if(!this.disable.links){
45294                 tb.add(
45295                     '-',
45296                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
45297                 );
45298             };
45299
45300             if(!this.disable.lists){
45301                 tb.add(
45302                     '-',
45303                     btn('insertorderedlist'),
45304                     btn('insertunorderedlist')
45305                 );
45306             }
45307             if(!this.disable.sourceEdit){
45308                 tb.add(
45309                     '-',
45310                     btn('sourceedit', true, function(btn){
45311                         this.toggleSourceEdit(btn.pressed);
45312                     })
45313                 );
45314             }
45315         //}
45316         
45317         var smenu = { };
45318         // special menu.. - needs to be tidied up..
45319         if (!this.disable.special) {
45320             smenu = {
45321                 text: "&#169;",
45322                 cls: 'x-edit-none',
45323                 
45324                 menu : {
45325                     items : []
45326                 }
45327             };
45328             for (var i =0; i < this.specialChars.length; i++) {
45329                 smenu.menu.items.push({
45330                     
45331                     html: this.specialChars[i],
45332                     handler: function(a,b) {
45333                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
45334                         //editor.insertAtCursor(a.html);
45335                         
45336                     },
45337                     tabIndex:-1
45338                 });
45339             }
45340             
45341             
45342             tb.add(smenu);
45343             
45344             
45345         }
45346         
45347         var cmenu = { };
45348         if (!this.disable.cleanStyles) {
45349             cmenu = {
45350                 cls: 'x-btn-icon x-btn-clear',
45351                 
45352                 menu : {
45353                     items : []
45354                 }
45355             };
45356             for (var i =0; i < this.cleanStyles.length; i++) {
45357                 cmenu.menu.items.push({
45358                     actiontype : this.cleanStyles[i],
45359                     html: 'Remove ' + this.cleanStyles[i],
45360                     handler: function(a,b) {
45361 //                        Roo.log(a);
45362 //                        Roo.log(b);
45363                         var c = Roo.get(editorcore.doc.body);
45364                         c.select('[style]').each(function(s) {
45365                             s.dom.style.removeProperty(a.actiontype);
45366                         });
45367                         editorcore.syncValue();
45368                     },
45369                     tabIndex:-1
45370                 });
45371             }
45372              cmenu.menu.items.push({
45373                 actiontype : 'tablewidths',
45374                 html: 'Remove Table Widths',
45375                 handler: function(a,b) {
45376                     editorcore.cleanTableWidths();
45377                     editorcore.syncValue();
45378                 },
45379                 tabIndex:-1
45380             });
45381             cmenu.menu.items.push({
45382                 actiontype : 'word',
45383                 html: 'Remove MS Word Formating',
45384                 handler: function(a,b) {
45385                     editorcore.cleanWord();
45386                     editorcore.syncValue();
45387                 },
45388                 tabIndex:-1
45389             });
45390             
45391             cmenu.menu.items.push({
45392                 actiontype : 'all',
45393                 html: 'Remove All Styles',
45394                 handler: function(a,b) {
45395                     
45396                     var c = Roo.get(editorcore.doc.body);
45397                     c.select('[style]').each(function(s) {
45398                         s.dom.removeAttribute('style');
45399                     });
45400                     editorcore.syncValue();
45401                 },
45402                 tabIndex:-1
45403             });
45404             
45405             cmenu.menu.items.push({
45406                 actiontype : 'all',
45407                 html: 'Remove All CSS Classes',
45408                 handler: function(a,b) {
45409                     
45410                     var c = Roo.get(editorcore.doc.body);
45411                     c.select('[class]').each(function(s) {
45412                         s.dom.className = '';
45413                     });
45414                     editorcore.syncValue();
45415                 },
45416                 tabIndex:-1
45417             });
45418             
45419              cmenu.menu.items.push({
45420                 actiontype : 'tidy',
45421                 html: 'Tidy HTML Source',
45422                 handler: function(a,b) {
45423                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
45424                     editorcore.syncValue();
45425                 },
45426                 tabIndex:-1
45427             });
45428             
45429             
45430             tb.add(cmenu);
45431         }
45432          
45433         if (!this.disable.specialElements) {
45434             var semenu = {
45435                 text: "Other;",
45436                 cls: 'x-edit-none',
45437                 menu : {
45438                     items : []
45439                 }
45440             };
45441             for (var i =0; i < this.specialElements.length; i++) {
45442                 semenu.menu.items.push(
45443                     Roo.apply({ 
45444                         handler: function(a,b) {
45445                             editor.insertAtCursor(this.ihtml);
45446                         }
45447                     }, this.specialElements[i])
45448                 );
45449                     
45450             }
45451             
45452             tb.add(semenu);
45453             
45454             
45455         }
45456          
45457         
45458         if (this.btns) {
45459             for(var i =0; i< this.btns.length;i++) {
45460                 var b = Roo.factory(this.btns[i],Roo.form);
45461                 b.cls =  'x-edit-none';
45462                 
45463                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
45464                     b.cls += ' x-init-enable';
45465                 }
45466                 
45467                 b.scope = editorcore;
45468                 tb.add(b);
45469             }
45470         
45471         }
45472         
45473         
45474         
45475         // disable everything...
45476         
45477         this.tb.items.each(function(item){
45478             
45479            if(
45480                 item.id != editorcore.frameId+ '-sourceedit' && 
45481                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
45482             ){
45483                 
45484                 item.disable();
45485             }
45486         });
45487         this.rendered = true;
45488         
45489         // the all the btns;
45490         editor.on('editorevent', this.updateToolbar, this);
45491         // other toolbars need to implement this..
45492         //editor.on('editmodechange', this.updateToolbar, this);
45493     },
45494     
45495     
45496     relayBtnCmd : function(btn) {
45497         this.editorcore.relayCmd(btn.cmd);
45498     },
45499     // private used internally
45500     createLink : function(){
45501         Roo.log("create link?");
45502         var url = prompt(this.createLinkText, this.defaultLinkValue);
45503         if(url && url != 'http:/'+'/'){
45504             this.editorcore.relayCmd('createlink', url);
45505         }
45506     },
45507
45508     
45509     /**
45510      * Protected method that will not generally be called directly. It triggers
45511      * a toolbar update by reading the markup state of the current selection in the editor.
45512      */
45513     updateToolbar: function(){
45514
45515         if(!this.editorcore.activated){
45516             this.editor.onFirstFocus();
45517             return;
45518         }
45519
45520         var btns = this.tb.items.map, 
45521             doc = this.editorcore.doc,
45522             frameId = this.editorcore.frameId;
45523
45524         if(!this.disable.font && !Roo.isSafari){
45525             /*
45526             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
45527             if(name != this.fontSelect.dom.value){
45528                 this.fontSelect.dom.value = name;
45529             }
45530             */
45531         }
45532         if(!this.disable.format){
45533             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
45534             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
45535             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
45536             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
45537         }
45538         if(!this.disable.alignments){
45539             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
45540             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
45541             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
45542         }
45543         if(!Roo.isSafari && !this.disable.lists){
45544             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
45545             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
45546         }
45547         
45548         var ans = this.editorcore.getAllAncestors();
45549         if (this.formatCombo) {
45550             
45551             
45552             var store = this.formatCombo.store;
45553             this.formatCombo.setValue("");
45554             for (var i =0; i < ans.length;i++) {
45555                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
45556                     // select it..
45557                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
45558                     break;
45559                 }
45560             }
45561         }
45562         
45563         
45564         
45565         // hides menus... - so this cant be on a menu...
45566         Roo.menu.MenuMgr.hideAll();
45567
45568         //this.editorsyncValue();
45569     },
45570    
45571     
45572     createFontOptions : function(){
45573         var buf = [], fs = this.fontFamilies, ff, lc;
45574         
45575         
45576         
45577         for(var i = 0, len = fs.length; i< len; i++){
45578             ff = fs[i];
45579             lc = ff.toLowerCase();
45580             buf.push(
45581                 '<option value="',lc,'" style="font-family:',ff,';"',
45582                     (this.defaultFont == lc ? ' selected="true">' : '>'),
45583                     ff,
45584                 '</option>'
45585             );
45586         }
45587         return buf.join('');
45588     },
45589     
45590     toggleSourceEdit : function(sourceEditMode){
45591         
45592         Roo.log("toolbar toogle");
45593         if(sourceEditMode === undefined){
45594             sourceEditMode = !this.sourceEditMode;
45595         }
45596         this.sourceEditMode = sourceEditMode === true;
45597         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
45598         // just toggle the button?
45599         if(btn.pressed !== this.sourceEditMode){
45600             btn.toggle(this.sourceEditMode);
45601             return;
45602         }
45603         
45604         if(sourceEditMode){
45605             Roo.log("disabling buttons");
45606             this.tb.items.each(function(item){
45607                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
45608                     item.disable();
45609                 }
45610             });
45611           
45612         }else{
45613             Roo.log("enabling buttons");
45614             if(this.editorcore.initialized){
45615                 this.tb.items.each(function(item){
45616                     item.enable();
45617                 });
45618             }
45619             
45620         }
45621         Roo.log("calling toggole on editor");
45622         // tell the editor that it's been pressed..
45623         this.editor.toggleSourceEdit(sourceEditMode);
45624        
45625     },
45626      /**
45627      * Object collection of toolbar tooltips for the buttons in the editor. The key
45628      * is the command id associated with that button and the value is a valid QuickTips object.
45629      * For example:
45630 <pre><code>
45631 {
45632     bold : {
45633         title: 'Bold (Ctrl+B)',
45634         text: 'Make the selected text bold.',
45635         cls: 'x-html-editor-tip'
45636     },
45637     italic : {
45638         title: 'Italic (Ctrl+I)',
45639         text: 'Make the selected text italic.',
45640         cls: 'x-html-editor-tip'
45641     },
45642     ...
45643 </code></pre>
45644     * @type Object
45645      */
45646     buttonTips : {
45647         bold : {
45648             title: 'Bold (Ctrl+B)',
45649             text: 'Make the selected text bold.',
45650             cls: 'x-html-editor-tip'
45651         },
45652         italic : {
45653             title: 'Italic (Ctrl+I)',
45654             text: 'Make the selected text italic.',
45655             cls: 'x-html-editor-tip'
45656         },
45657         underline : {
45658             title: 'Underline (Ctrl+U)',
45659             text: 'Underline the selected text.',
45660             cls: 'x-html-editor-tip'
45661         },
45662         strikethrough : {
45663             title: 'Strikethrough',
45664             text: 'Strikethrough the selected text.',
45665             cls: 'x-html-editor-tip'
45666         },
45667         increasefontsize : {
45668             title: 'Grow Text',
45669             text: 'Increase the font size.',
45670             cls: 'x-html-editor-tip'
45671         },
45672         decreasefontsize : {
45673             title: 'Shrink Text',
45674             text: 'Decrease the font size.',
45675             cls: 'x-html-editor-tip'
45676         },
45677         backcolor : {
45678             title: 'Text Highlight Color',
45679             text: 'Change the background color of the selected text.',
45680             cls: 'x-html-editor-tip'
45681         },
45682         forecolor : {
45683             title: 'Font Color',
45684             text: 'Change the color of the selected text.',
45685             cls: 'x-html-editor-tip'
45686         },
45687         justifyleft : {
45688             title: 'Align Text Left',
45689             text: 'Align text to the left.',
45690             cls: 'x-html-editor-tip'
45691         },
45692         justifycenter : {
45693             title: 'Center Text',
45694             text: 'Center text in the editor.',
45695             cls: 'x-html-editor-tip'
45696         },
45697         justifyright : {
45698             title: 'Align Text Right',
45699             text: 'Align text to the right.',
45700             cls: 'x-html-editor-tip'
45701         },
45702         insertunorderedlist : {
45703             title: 'Bullet List',
45704             text: 'Start a bulleted list.',
45705             cls: 'x-html-editor-tip'
45706         },
45707         insertorderedlist : {
45708             title: 'Numbered List',
45709             text: 'Start a numbered list.',
45710             cls: 'x-html-editor-tip'
45711         },
45712         createlink : {
45713             title: 'Hyperlink',
45714             text: 'Make the selected text a hyperlink.',
45715             cls: 'x-html-editor-tip'
45716         },
45717         sourceedit : {
45718             title: 'Source Edit',
45719             text: 'Switch to source editing mode.',
45720             cls: 'x-html-editor-tip'
45721         }
45722     },
45723     // private
45724     onDestroy : function(){
45725         if(this.rendered){
45726             
45727             this.tb.items.each(function(item){
45728                 if(item.menu){
45729                     item.menu.removeAll();
45730                     if(item.menu.el){
45731                         item.menu.el.destroy();
45732                     }
45733                 }
45734                 item.destroy();
45735             });
45736              
45737         }
45738     },
45739     onFirstFocus: function() {
45740         this.tb.items.each(function(item){
45741            item.enable();
45742         });
45743     }
45744 });
45745
45746
45747
45748
45749 // <script type="text/javascript">
45750 /*
45751  * Based on
45752  * Ext JS Library 1.1.1
45753  * Copyright(c) 2006-2007, Ext JS, LLC.
45754  *  
45755  
45756  */
45757
45758  
45759 /**
45760  * @class Roo.form.HtmlEditor.ToolbarContext
45761  * Context Toolbar
45762  * 
45763  * Usage:
45764  *
45765  new Roo.form.HtmlEditor({
45766     ....
45767     toolbars : [
45768         { xtype: 'ToolbarStandard', styles : {} }
45769         { xtype: 'ToolbarContext', disable : {} }
45770     ]
45771 })
45772
45773      
45774  * 
45775  * @config : {Object} disable List of elements to disable.. (not done yet.)
45776  * @config : {Object} styles  Map of styles available.
45777  * 
45778  */
45779
45780 Roo.form.HtmlEditor.ToolbarContext = function(config)
45781 {
45782     
45783     Roo.apply(this, config);
45784     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45785     // dont call parent... till later.
45786     this.styles = this.styles || {};
45787 }
45788
45789  
45790
45791 Roo.form.HtmlEditor.ToolbarContext.types = {
45792     'IMG' : {
45793         width : {
45794             title: "Width",
45795             width: 40
45796         },
45797         height:  {
45798             title: "Height",
45799             width: 40
45800         },
45801         align: {
45802             title: "Align",
45803             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
45804             width : 80
45805             
45806         },
45807         border: {
45808             title: "Border",
45809             width: 40
45810         },
45811         alt: {
45812             title: "Alt",
45813             width: 120
45814         },
45815         src : {
45816             title: "Src",
45817             width: 220
45818         }
45819         
45820     },
45821     'A' : {
45822         name : {
45823             title: "Name",
45824             width: 50
45825         },
45826         target:  {
45827             title: "Target",
45828             width: 120
45829         },
45830         href:  {
45831             title: "Href",
45832             width: 220
45833         } // border?
45834         
45835     },
45836     'TABLE' : {
45837         rows : {
45838             title: "Rows",
45839             width: 20
45840         },
45841         cols : {
45842             title: "Cols",
45843             width: 20
45844         },
45845         width : {
45846             title: "Width",
45847             width: 40
45848         },
45849         height : {
45850             title: "Height",
45851             width: 40
45852         },
45853         border : {
45854             title: "Border",
45855             width: 20
45856         }
45857     },
45858     'TD' : {
45859         width : {
45860             title: "Width",
45861             width: 40
45862         },
45863         height : {
45864             title: "Height",
45865             width: 40
45866         },   
45867         align: {
45868             title: "Align",
45869             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
45870             width: 80
45871         },
45872         valign: {
45873             title: "Valign",
45874             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
45875             width: 80
45876         },
45877         colspan: {
45878             title: "Colspan",
45879             width: 20
45880             
45881         },
45882          'font-family'  : {
45883             title : "Font",
45884             style : 'fontFamily',
45885             displayField: 'display',
45886             optname : 'font-family',
45887             width: 140
45888         }
45889     },
45890     'INPUT' : {
45891         name : {
45892             title: "name",
45893             width: 120
45894         },
45895         value : {
45896             title: "Value",
45897             width: 120
45898         },
45899         width : {
45900             title: "Width",
45901             width: 40
45902         }
45903     },
45904     'LABEL' : {
45905         'for' : {
45906             title: "For",
45907             width: 120
45908         }
45909     },
45910     'TEXTAREA' : {
45911           name : {
45912             title: "name",
45913             width: 120
45914         },
45915         rows : {
45916             title: "Rows",
45917             width: 20
45918         },
45919         cols : {
45920             title: "Cols",
45921             width: 20
45922         }
45923     },
45924     'SELECT' : {
45925         name : {
45926             title: "name",
45927             width: 120
45928         },
45929         selectoptions : {
45930             title: "Options",
45931             width: 200
45932         }
45933     },
45934     
45935     // should we really allow this??
45936     // should this just be 
45937     'BODY' : {
45938         title : {
45939             title: "Title",
45940             width: 200,
45941             disabled : true
45942         }
45943     },
45944     'SPAN' : {
45945         'font-family'  : {
45946             title : "Font",
45947             style : 'fontFamily',
45948             displayField: 'display',
45949             optname : 'font-family',
45950             width: 140
45951         }
45952     },
45953     'DIV' : {
45954         'font-family'  : {
45955             title : "Font",
45956             style : 'fontFamily',
45957             displayField: 'display',
45958             optname : 'font-family',
45959             width: 140
45960         }
45961     },
45962      'P' : {
45963         'font-family'  : {
45964             title : "Font",
45965             style : 'fontFamily',
45966             displayField: 'display',
45967             optname : 'font-family',
45968             width: 140
45969         }
45970     },
45971     
45972     '*' : {
45973         // empty..
45974     }
45975
45976 };
45977
45978 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
45979 Roo.form.HtmlEditor.ToolbarContext.stores = false;
45980
45981 Roo.form.HtmlEditor.ToolbarContext.options = {
45982         'font-family'  : [ 
45983                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
45984                 [ 'Courier New', 'Courier New'],
45985                 [ 'Tahoma', 'Tahoma'],
45986                 [ 'Times New Roman,serif', 'Times'],
45987                 [ 'Verdana','Verdana' ]
45988         ]
45989 };
45990
45991 // fixme - these need to be configurable..
45992  
45993
45994 //Roo.form.HtmlEditor.ToolbarContext.types
45995
45996
45997 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
45998     
45999     tb: false,
46000     
46001     rendered: false,
46002     
46003     editor : false,
46004     editorcore : false,
46005     /**
46006      * @cfg {Object} disable  List of toolbar elements to disable
46007          
46008      */
46009     disable : false,
46010     /**
46011      * @cfg {Object} styles List of styles 
46012      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
46013      *
46014      * These must be defined in the page, so they get rendered correctly..
46015      * .headline { }
46016      * TD.underline { }
46017      * 
46018      */
46019     styles : false,
46020     
46021     options: false,
46022     
46023     toolbars : false,
46024     
46025     init : function(editor)
46026     {
46027         this.editor = editor;
46028         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46029         var editorcore = this.editorcore;
46030         
46031         var fid = editorcore.frameId;
46032         var etb = this;
46033         function btn(id, toggle, handler){
46034             var xid = fid + '-'+ id ;
46035             return {
46036                 id : xid,
46037                 cmd : id,
46038                 cls : 'x-btn-icon x-edit-'+id,
46039                 enableToggle:toggle !== false,
46040                 scope: editorcore, // was editor...
46041                 handler:handler||editorcore.relayBtnCmd,
46042                 clickEvent:'mousedown',
46043                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46044                 tabIndex:-1
46045             };
46046         }
46047         // create a new element.
46048         var wdiv = editor.wrap.createChild({
46049                 tag: 'div'
46050             }, editor.wrap.dom.firstChild.nextSibling, true);
46051         
46052         // can we do this more than once??
46053         
46054          // stop form submits
46055       
46056  
46057         // disable everything...
46058         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46059         this.toolbars = {};
46060            
46061         for (var i in  ty) {
46062           
46063             this.toolbars[i] = this.buildToolbar(ty[i],i);
46064         }
46065         this.tb = this.toolbars.BODY;
46066         this.tb.el.show();
46067         this.buildFooter();
46068         this.footer.show();
46069         editor.on('hide', function( ) { this.footer.hide() }, this);
46070         editor.on('show', function( ) { this.footer.show() }, this);
46071         
46072          
46073         this.rendered = true;
46074         
46075         // the all the btns;
46076         editor.on('editorevent', this.updateToolbar, this);
46077         // other toolbars need to implement this..
46078         //editor.on('editmodechange', this.updateToolbar, this);
46079     },
46080     
46081     
46082     
46083     /**
46084      * Protected method that will not generally be called directly. It triggers
46085      * a toolbar update by reading the markup state of the current selection in the editor.
46086      *
46087      * Note you can force an update by calling on('editorevent', scope, false)
46088      */
46089     updateToolbar: function(editor,ev,sel){
46090
46091         //Roo.log(ev);
46092         // capture mouse up - this is handy for selecting images..
46093         // perhaps should go somewhere else...
46094         if(!this.editorcore.activated){
46095              this.editor.onFirstFocus();
46096             return;
46097         }
46098         
46099         
46100         
46101         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46102         // selectNode - might want to handle IE?
46103         if (ev &&
46104             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46105             ev.target && ev.target.tagName == 'IMG') {
46106             // they have click on an image...
46107             // let's see if we can change the selection...
46108             sel = ev.target;
46109          
46110               var nodeRange = sel.ownerDocument.createRange();
46111             try {
46112                 nodeRange.selectNode(sel);
46113             } catch (e) {
46114                 nodeRange.selectNodeContents(sel);
46115             }
46116             //nodeRange.collapse(true);
46117             var s = this.editorcore.win.getSelection();
46118             s.removeAllRanges();
46119             s.addRange(nodeRange);
46120         }  
46121         
46122       
46123         var updateFooter = sel ? false : true;
46124         
46125         
46126         var ans = this.editorcore.getAllAncestors();
46127         
46128         // pick
46129         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46130         
46131         if (!sel) { 
46132             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
46133             sel = sel ? sel : this.editorcore.doc.body;
46134             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
46135             
46136         }
46137         // pick a menu that exists..
46138         var tn = sel.tagName.toUpperCase();
46139         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
46140         
46141         tn = sel.tagName.toUpperCase();
46142         
46143         var lastSel = this.tb.selectedNode;
46144         
46145         this.tb.selectedNode = sel;
46146         
46147         // if current menu does not match..
46148         
46149         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
46150                 
46151             this.tb.el.hide();
46152             ///console.log("show: " + tn);
46153             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
46154             this.tb.el.show();
46155             // update name
46156             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
46157             
46158             
46159             // update attributes
46160             if (this.tb.fields) {
46161                 this.tb.fields.each(function(e) {
46162                     if (e.stylename) {
46163                         e.setValue(sel.style[e.stylename]);
46164                         return;
46165                     } 
46166                    e.setValue(sel.getAttribute(e.attrname));
46167                 });
46168             }
46169             
46170             var hasStyles = false;
46171             for(var i in this.styles) {
46172                 hasStyles = true;
46173                 break;
46174             }
46175             
46176             // update styles
46177             if (hasStyles) { 
46178                 var st = this.tb.fields.item(0);
46179                 
46180                 st.store.removeAll();
46181                
46182                 
46183                 var cn = sel.className.split(/\s+/);
46184                 
46185                 var avs = [];
46186                 if (this.styles['*']) {
46187                     
46188                     Roo.each(this.styles['*'], function(v) {
46189                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46190                     });
46191                 }
46192                 if (this.styles[tn]) { 
46193                     Roo.each(this.styles[tn], function(v) {
46194                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46195                     });
46196                 }
46197                 
46198                 st.store.loadData(avs);
46199                 st.collapse();
46200                 st.setValue(cn);
46201             }
46202             // flag our selected Node.
46203             this.tb.selectedNode = sel;
46204            
46205            
46206             Roo.menu.MenuMgr.hideAll();
46207
46208         }
46209         
46210         if (!updateFooter) {
46211             //this.footDisp.dom.innerHTML = ''; 
46212             return;
46213         }
46214         // update the footer
46215         //
46216         var html = '';
46217         
46218         this.footerEls = ans.reverse();
46219         Roo.each(this.footerEls, function(a,i) {
46220             if (!a) { return; }
46221             html += html.length ? ' &gt; '  :  '';
46222             
46223             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
46224             
46225         });
46226        
46227         // 
46228         var sz = this.footDisp.up('td').getSize();
46229         this.footDisp.dom.style.width = (sz.width -10) + 'px';
46230         this.footDisp.dom.style.marginLeft = '5px';
46231         
46232         this.footDisp.dom.style.overflow = 'hidden';
46233         
46234         this.footDisp.dom.innerHTML = html;
46235             
46236         //this.editorsyncValue();
46237     },
46238      
46239     
46240    
46241        
46242     // private
46243     onDestroy : function(){
46244         if(this.rendered){
46245             
46246             this.tb.items.each(function(item){
46247                 if(item.menu){
46248                     item.menu.removeAll();
46249                     if(item.menu.el){
46250                         item.menu.el.destroy();
46251                     }
46252                 }
46253                 item.destroy();
46254             });
46255              
46256         }
46257     },
46258     onFirstFocus: function() {
46259         // need to do this for all the toolbars..
46260         this.tb.items.each(function(item){
46261            item.enable();
46262         });
46263     },
46264     buildToolbar: function(tlist, nm)
46265     {
46266         var editor = this.editor;
46267         var editorcore = this.editorcore;
46268          // create a new element.
46269         var wdiv = editor.wrap.createChild({
46270                 tag: 'div'
46271             }, editor.wrap.dom.firstChild.nextSibling, true);
46272         
46273        
46274         var tb = new Roo.Toolbar(wdiv);
46275         // add the name..
46276         
46277         tb.add(nm+ ":&nbsp;");
46278         
46279         var styles = [];
46280         for(var i in this.styles) {
46281             styles.push(i);
46282         }
46283         
46284         // styles...
46285         if (styles && styles.length) {
46286             
46287             // this needs a multi-select checkbox...
46288             tb.addField( new Roo.form.ComboBox({
46289                 store: new Roo.data.SimpleStore({
46290                     id : 'val',
46291                     fields: ['val', 'selected'],
46292                     data : [] 
46293                 }),
46294                 name : '-roo-edit-className',
46295                 attrname : 'className',
46296                 displayField: 'val',
46297                 typeAhead: false,
46298                 mode: 'local',
46299                 editable : false,
46300                 triggerAction: 'all',
46301                 emptyText:'Select Style',
46302                 selectOnFocus:true,
46303                 width: 130,
46304                 listeners : {
46305                     'select': function(c, r, i) {
46306                         // initial support only for on class per el..
46307                         tb.selectedNode.className =  r ? r.get('val') : '';
46308                         editorcore.syncValue();
46309                     }
46310                 }
46311     
46312             }));
46313         }
46314         
46315         var tbc = Roo.form.HtmlEditor.ToolbarContext;
46316         var tbops = tbc.options;
46317         
46318         for (var i in tlist) {
46319             
46320             var item = tlist[i];
46321             tb.add(item.title + ":&nbsp;");
46322             
46323             
46324             //optname == used so you can configure the options available..
46325             var opts = item.opts ? item.opts : false;
46326             if (item.optname) {
46327                 opts = tbops[item.optname];
46328            
46329             }
46330             
46331             if (opts) {
46332                 // opts == pulldown..
46333                 tb.addField( new Roo.form.ComboBox({
46334                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
46335                         id : 'val',
46336                         fields: ['val', 'display'],
46337                         data : opts  
46338                     }),
46339                     name : '-roo-edit-' + i,
46340                     attrname : i,
46341                     stylename : item.style ? item.style : false,
46342                     displayField: item.displayField ? item.displayField : 'val',
46343                     valueField :  'val',
46344                     typeAhead: false,
46345                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
46346                     editable : false,
46347                     triggerAction: 'all',
46348                     emptyText:'Select',
46349                     selectOnFocus:true,
46350                     width: item.width ? item.width  : 130,
46351                     listeners : {
46352                         'select': function(c, r, i) {
46353                             if (c.stylename) {
46354                                 tb.selectedNode.style[c.stylename] =  r.get('val');
46355                                 return;
46356                             }
46357                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
46358                         }
46359                     }
46360
46361                 }));
46362                 continue;
46363                     
46364                  
46365                 
46366                 tb.addField( new Roo.form.TextField({
46367                     name: i,
46368                     width: 100,
46369                     //allowBlank:false,
46370                     value: ''
46371                 }));
46372                 continue;
46373             }
46374             tb.addField( new Roo.form.TextField({
46375                 name: '-roo-edit-' + i,
46376                 attrname : i,
46377                 
46378                 width: item.width,
46379                 //allowBlank:true,
46380                 value: '',
46381                 listeners: {
46382                     'change' : function(f, nv, ov) {
46383                         tb.selectedNode.setAttribute(f.attrname, nv);
46384                         editorcore.syncValue();
46385                     }
46386                 }
46387             }));
46388              
46389         }
46390         
46391         var _this = this;
46392         
46393         if(nm == 'BODY'){
46394             tb.addSeparator();
46395         
46396             tb.addButton( {
46397                 text: 'Stylesheets',
46398
46399                 listeners : {
46400                     click : function ()
46401                     {
46402                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
46403                     }
46404                 }
46405             });
46406         }
46407         
46408         tb.addFill();
46409         tb.addButton( {
46410             text: 'Remove Tag',
46411     
46412             listeners : {
46413                 click : function ()
46414                 {
46415                     // remove
46416                     // undo does not work.
46417                      
46418                     var sn = tb.selectedNode;
46419                     
46420                     var pn = sn.parentNode;
46421                     
46422                     var stn =  sn.childNodes[0];
46423                     var en = sn.childNodes[sn.childNodes.length - 1 ];
46424                     while (sn.childNodes.length) {
46425                         var node = sn.childNodes[0];
46426                         sn.removeChild(node);
46427                         //Roo.log(node);
46428                         pn.insertBefore(node, sn);
46429                         
46430                     }
46431                     pn.removeChild(sn);
46432                     var range = editorcore.createRange();
46433         
46434                     range.setStart(stn,0);
46435                     range.setEnd(en,0); //????
46436                     //range.selectNode(sel);
46437                     
46438                     
46439                     var selection = editorcore.getSelection();
46440                     selection.removeAllRanges();
46441                     selection.addRange(range);
46442                     
46443                     
46444                     
46445                     //_this.updateToolbar(null, null, pn);
46446                     _this.updateToolbar(null, null, null);
46447                     _this.footDisp.dom.innerHTML = ''; 
46448                 }
46449             }
46450             
46451                     
46452                 
46453             
46454         });
46455         
46456         
46457         tb.el.on('click', function(e){
46458             e.preventDefault(); // what does this do?
46459         });
46460         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
46461         tb.el.hide();
46462         tb.name = nm;
46463         // dont need to disable them... as they will get hidden
46464         return tb;
46465          
46466         
46467     },
46468     buildFooter : function()
46469     {
46470         
46471         var fel = this.editor.wrap.createChild();
46472         this.footer = new Roo.Toolbar(fel);
46473         // toolbar has scrolly on left / right?
46474         var footDisp= new Roo.Toolbar.Fill();
46475         var _t = this;
46476         this.footer.add(
46477             {
46478                 text : '&lt;',
46479                 xtype: 'Button',
46480                 handler : function() {
46481                     _t.footDisp.scrollTo('left',0,true)
46482                 }
46483             }
46484         );
46485         this.footer.add( footDisp );
46486         this.footer.add( 
46487             {
46488                 text : '&gt;',
46489                 xtype: 'Button',
46490                 handler : function() {
46491                     // no animation..
46492                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
46493                 }
46494             }
46495         );
46496         var fel = Roo.get(footDisp.el);
46497         fel.addClass('x-editor-context');
46498         this.footDispWrap = fel; 
46499         this.footDispWrap.overflow  = 'hidden';
46500         
46501         this.footDisp = fel.createChild();
46502         this.footDispWrap.on('click', this.onContextClick, this)
46503         
46504         
46505     },
46506     onContextClick : function (ev,dom)
46507     {
46508         ev.preventDefault();
46509         var  cn = dom.className;
46510         //Roo.log(cn);
46511         if (!cn.match(/x-ed-loc-/)) {
46512             return;
46513         }
46514         var n = cn.split('-').pop();
46515         var ans = this.footerEls;
46516         var sel = ans[n];
46517         
46518          // pick
46519         var range = this.editorcore.createRange();
46520         
46521         range.selectNodeContents(sel);
46522         //range.selectNode(sel);
46523         
46524         
46525         var selection = this.editorcore.getSelection();
46526         selection.removeAllRanges();
46527         selection.addRange(range);
46528         
46529         
46530         
46531         this.updateToolbar(null, null, sel);
46532         
46533         
46534     }
46535     
46536     
46537     
46538     
46539     
46540 });
46541
46542
46543
46544
46545
46546 /*
46547  * Based on:
46548  * Ext JS Library 1.1.1
46549  * Copyright(c) 2006-2007, Ext JS, LLC.
46550  *
46551  * Originally Released Under LGPL - original licence link has changed is not relivant.
46552  *
46553  * Fork - LGPL
46554  * <script type="text/javascript">
46555  */
46556  
46557 /**
46558  * @class Roo.form.BasicForm
46559  * @extends Roo.util.Observable
46560  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
46561  * @constructor
46562  * @param {String/HTMLElement/Roo.Element} el The form element or its id
46563  * @param {Object} config Configuration options
46564  */
46565 Roo.form.BasicForm = function(el, config){
46566     this.allItems = [];
46567     this.childForms = [];
46568     Roo.apply(this, config);
46569     /*
46570      * The Roo.form.Field items in this form.
46571      * @type MixedCollection
46572      */
46573      
46574      
46575     this.items = new Roo.util.MixedCollection(false, function(o){
46576         return o.id || (o.id = Roo.id());
46577     });
46578     this.addEvents({
46579         /**
46580          * @event beforeaction
46581          * Fires before any action is performed. Return false to cancel the action.
46582          * @param {Form} this
46583          * @param {Action} action The action to be performed
46584          */
46585         beforeaction: true,
46586         /**
46587          * @event actionfailed
46588          * Fires when an action fails.
46589          * @param {Form} this
46590          * @param {Action} action The action that failed
46591          */
46592         actionfailed : true,
46593         /**
46594          * @event actioncomplete
46595          * Fires when an action is completed.
46596          * @param {Form} this
46597          * @param {Action} action The action that completed
46598          */
46599         actioncomplete : true
46600     });
46601     if(el){
46602         this.initEl(el);
46603     }
46604     Roo.form.BasicForm.superclass.constructor.call(this);
46605 };
46606
46607 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
46608     /**
46609      * @cfg {String} method
46610      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
46611      */
46612     /**
46613      * @cfg {DataReader} reader
46614      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
46615      * This is optional as there is built-in support for processing JSON.
46616      */
46617     /**
46618      * @cfg {DataReader} errorReader
46619      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
46620      * This is completely optional as there is built-in support for processing JSON.
46621      */
46622     /**
46623      * @cfg {String} url
46624      * The URL to use for form actions if one isn't supplied in the action options.
46625      */
46626     /**
46627      * @cfg {Boolean} fileUpload
46628      * Set to true if this form is a file upload.
46629      */
46630      
46631     /**
46632      * @cfg {Object} baseParams
46633      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
46634      */
46635      /**
46636      
46637     /**
46638      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
46639      */
46640     timeout: 30,
46641
46642     // private
46643     activeAction : null,
46644
46645     /**
46646      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
46647      * or setValues() data instead of when the form was first created.
46648      */
46649     trackResetOnLoad : false,
46650     
46651     
46652     /**
46653      * childForms - used for multi-tab forms
46654      * @type {Array}
46655      */
46656     childForms : false,
46657     
46658     /**
46659      * allItems - full list of fields.
46660      * @type {Array}
46661      */
46662     allItems : false,
46663     
46664     /**
46665      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
46666      * element by passing it or its id or mask the form itself by passing in true.
46667      * @type Mixed
46668      */
46669     waitMsgTarget : false,
46670
46671     // private
46672     initEl : function(el){
46673         this.el = Roo.get(el);
46674         this.id = this.el.id || Roo.id();
46675         this.el.on('submit', this.onSubmit, this);
46676         this.el.addClass('x-form');
46677     },
46678
46679     // private
46680     onSubmit : function(e){
46681         e.stopEvent();
46682     },
46683
46684     /**
46685      * Returns true if client-side validation on the form is successful.
46686      * @return Boolean
46687      */
46688     isValid : function(){
46689         var valid = true;
46690         this.items.each(function(f){
46691            if(!f.validate()){
46692                valid = false;
46693            }
46694         });
46695         return valid;
46696     },
46697
46698     /**
46699      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
46700      * @return Boolean
46701      */
46702     isDirty : function(){
46703         var dirty = false;
46704         this.items.each(function(f){
46705            if(f.isDirty()){
46706                dirty = true;
46707                return false;
46708            }
46709         });
46710         return dirty;
46711     },
46712     
46713     /**
46714      * Returns true if any fields in this form have changed since their original load. (New version)
46715      * @return Boolean
46716      */
46717     
46718     hasChanged : function()
46719     {
46720         var dirty = false;
46721         this.items.each(function(f){
46722            if(f.hasChanged()){
46723                dirty = true;
46724                return false;
46725            }
46726         });
46727         return dirty;
46728         
46729     },
46730     /**
46731      * Resets all hasChanged to 'false' -
46732      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
46733      * So hasChanged storage is only to be used for this purpose
46734      * @return Boolean
46735      */
46736     resetHasChanged : function()
46737     {
46738         this.items.each(function(f){
46739            f.resetHasChanged();
46740         });
46741         
46742     },
46743     
46744     
46745     /**
46746      * Performs a predefined action (submit or load) or custom actions you define on this form.
46747      * @param {String} actionName The name of the action type
46748      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
46749      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
46750      * accept other config options):
46751      * <pre>
46752 Property          Type             Description
46753 ----------------  ---------------  ----------------------------------------------------------------------------------
46754 url               String           The url for the action (defaults to the form's url)
46755 method            String           The form method to use (defaults to the form's method, or POST if not defined)
46756 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
46757 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
46758                                    validate the form on the client (defaults to false)
46759      * </pre>
46760      * @return {BasicForm} this
46761      */
46762     doAction : function(action, options){
46763         if(typeof action == 'string'){
46764             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
46765         }
46766         if(this.fireEvent('beforeaction', this, action) !== false){
46767             this.beforeAction(action);
46768             action.run.defer(100, action);
46769         }
46770         return this;
46771     },
46772
46773     /**
46774      * Shortcut to do a submit action.
46775      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46776      * @return {BasicForm} this
46777      */
46778     submit : function(options){
46779         this.doAction('submit', options);
46780         return this;
46781     },
46782
46783     /**
46784      * Shortcut to do a load action.
46785      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46786      * @return {BasicForm} this
46787      */
46788     load : function(options){
46789         this.doAction('load', options);
46790         return this;
46791     },
46792
46793     /**
46794      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
46795      * @param {Record} record The record to edit
46796      * @return {BasicForm} this
46797      */
46798     updateRecord : function(record){
46799         record.beginEdit();
46800         var fs = record.fields;
46801         fs.each(function(f){
46802             var field = this.findField(f.name);
46803             if(field){
46804                 record.set(f.name, field.getValue());
46805             }
46806         }, this);
46807         record.endEdit();
46808         return this;
46809     },
46810
46811     /**
46812      * Loads an Roo.data.Record into this form.
46813      * @param {Record} record The record to load
46814      * @return {BasicForm} this
46815      */
46816     loadRecord : function(record){
46817         this.setValues(record.data);
46818         return this;
46819     },
46820
46821     // private
46822     beforeAction : function(action){
46823         var o = action.options;
46824         
46825        
46826         if(this.waitMsgTarget === true){
46827             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
46828         }else if(this.waitMsgTarget){
46829             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
46830             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
46831         }else {
46832             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
46833         }
46834          
46835     },
46836
46837     // private
46838     afterAction : function(action, success){
46839         this.activeAction = null;
46840         var o = action.options;
46841         
46842         if(this.waitMsgTarget === true){
46843             this.el.unmask();
46844         }else if(this.waitMsgTarget){
46845             this.waitMsgTarget.unmask();
46846         }else{
46847             Roo.MessageBox.updateProgress(1);
46848             Roo.MessageBox.hide();
46849         }
46850          
46851         if(success){
46852             if(o.reset){
46853                 this.reset();
46854             }
46855             Roo.callback(o.success, o.scope, [this, action]);
46856             this.fireEvent('actioncomplete', this, action);
46857             
46858         }else{
46859             
46860             // failure condition..
46861             // we have a scenario where updates need confirming.
46862             // eg. if a locking scenario exists..
46863             // we look for { errors : { needs_confirm : true }} in the response.
46864             if (
46865                 (typeof(action.result) != 'undefined')  &&
46866                 (typeof(action.result.errors) != 'undefined')  &&
46867                 (typeof(action.result.errors.needs_confirm) != 'undefined')
46868            ){
46869                 var _t = this;
46870                 Roo.MessageBox.confirm(
46871                     "Change requires confirmation",
46872                     action.result.errorMsg,
46873                     function(r) {
46874                         if (r != 'yes') {
46875                             return;
46876                         }
46877                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
46878                     }
46879                     
46880                 );
46881                 
46882                 
46883                 
46884                 return;
46885             }
46886             
46887             Roo.callback(o.failure, o.scope, [this, action]);
46888             // show an error message if no failed handler is set..
46889             if (!this.hasListener('actionfailed')) {
46890                 Roo.MessageBox.alert("Error",
46891                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
46892                         action.result.errorMsg :
46893                         "Saving Failed, please check your entries or try again"
46894                 );
46895             }
46896             
46897             this.fireEvent('actionfailed', this, action);
46898         }
46899         
46900     },
46901
46902     /**
46903      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
46904      * @param {String} id The value to search for
46905      * @return Field
46906      */
46907     findField : function(id){
46908         var field = this.items.get(id);
46909         if(!field){
46910             this.items.each(function(f){
46911                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
46912                     field = f;
46913                     return false;
46914                 }
46915             });
46916         }
46917         return field || null;
46918     },
46919
46920     /**
46921      * Add a secondary form to this one, 
46922      * Used to provide tabbed forms. One form is primary, with hidden values 
46923      * which mirror the elements from the other forms.
46924      * 
46925      * @param {Roo.form.Form} form to add.
46926      * 
46927      */
46928     addForm : function(form)
46929     {
46930        
46931         if (this.childForms.indexOf(form) > -1) {
46932             // already added..
46933             return;
46934         }
46935         this.childForms.push(form);
46936         var n = '';
46937         Roo.each(form.allItems, function (fe) {
46938             
46939             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
46940             if (this.findField(n)) { // already added..
46941                 return;
46942             }
46943             var add = new Roo.form.Hidden({
46944                 name : n
46945             });
46946             add.render(this.el);
46947             
46948             this.add( add );
46949         }, this);
46950         
46951     },
46952     /**
46953      * Mark fields in this form invalid in bulk.
46954      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
46955      * @return {BasicForm} this
46956      */
46957     markInvalid : function(errors){
46958         if(errors instanceof Array){
46959             for(var i = 0, len = errors.length; i < len; i++){
46960                 var fieldError = errors[i];
46961                 var f = this.findField(fieldError.id);
46962                 if(f){
46963                     f.markInvalid(fieldError.msg);
46964                 }
46965             }
46966         }else{
46967             var field, id;
46968             for(id in errors){
46969                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
46970                     field.markInvalid(errors[id]);
46971                 }
46972             }
46973         }
46974         Roo.each(this.childForms || [], function (f) {
46975             f.markInvalid(errors);
46976         });
46977         
46978         return this;
46979     },
46980
46981     /**
46982      * Set values for fields in this form in bulk.
46983      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
46984      * @return {BasicForm} this
46985      */
46986     setValues : function(values){
46987         if(values instanceof Array){ // array of objects
46988             for(var i = 0, len = values.length; i < len; i++){
46989                 var v = values[i];
46990                 var f = this.findField(v.id);
46991                 if(f){
46992                     f.setValue(v.value);
46993                     if(this.trackResetOnLoad){
46994                         f.originalValue = f.getValue();
46995                     }
46996                 }
46997             }
46998         }else{ // object hash
46999             var field, id;
47000             for(id in values){
47001                 if(typeof values[id] != 'function' && (field = this.findField(id))){
47002                     
47003                     if (field.setFromData && 
47004                         field.valueField && 
47005                         field.displayField &&
47006                         // combos' with local stores can 
47007                         // be queried via setValue()
47008                         // to set their value..
47009                         (field.store && !field.store.isLocal)
47010                         ) {
47011                         // it's a combo
47012                         var sd = { };
47013                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
47014                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
47015                         field.setFromData(sd);
47016                         
47017                     } else {
47018                         field.setValue(values[id]);
47019                     }
47020                     
47021                     
47022                     if(this.trackResetOnLoad){
47023                         field.originalValue = field.getValue();
47024                     }
47025                 }
47026             }
47027         }
47028         this.resetHasChanged();
47029         
47030         
47031         Roo.each(this.childForms || [], function (f) {
47032             f.setValues(values);
47033             f.resetHasChanged();
47034         });
47035                 
47036         return this;
47037     },
47038
47039     /**
47040      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
47041      * they are returned as an array.
47042      * @param {Boolean} asString
47043      * @return {Object}
47044      */
47045     getValues : function(asString){
47046         if (this.childForms) {
47047             // copy values from the child forms
47048             Roo.each(this.childForms, function (f) {
47049                 this.setValues(f.getValues());
47050             }, this);
47051         }
47052         
47053         
47054         
47055         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47056         if(asString === true){
47057             return fs;
47058         }
47059         return Roo.urlDecode(fs);
47060     },
47061     
47062     /**
47063      * Returns the fields in this form as an object with key/value pairs. 
47064      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
47065      * @return {Object}
47066      */
47067     getFieldValues : function(with_hidden)
47068     {
47069         if (this.childForms) {
47070             // copy values from the child forms
47071             // should this call getFieldValues - probably not as we do not currently copy
47072             // hidden fields when we generate..
47073             Roo.each(this.childForms, function (f) {
47074                 this.setValues(f.getValues());
47075             }, this);
47076         }
47077         
47078         var ret = {};
47079         this.items.each(function(f){
47080             if (!f.getName()) {
47081                 return;
47082             }
47083             var v = f.getValue();
47084             if (f.inputType =='radio') {
47085                 if (typeof(ret[f.getName()]) == 'undefined') {
47086                     ret[f.getName()] = ''; // empty..
47087                 }
47088                 
47089                 if (!f.el.dom.checked) {
47090                     return;
47091                     
47092                 }
47093                 v = f.el.dom.value;
47094                 
47095             }
47096             
47097             // not sure if this supported any more..
47098             if ((typeof(v) == 'object') && f.getRawValue) {
47099                 v = f.getRawValue() ; // dates..
47100             }
47101             // combo boxes where name != hiddenName...
47102             if (f.name != f.getName()) {
47103                 ret[f.name] = f.getRawValue();
47104             }
47105             ret[f.getName()] = v;
47106         });
47107         
47108         return ret;
47109     },
47110
47111     /**
47112      * Clears all invalid messages in this form.
47113      * @return {BasicForm} this
47114      */
47115     clearInvalid : function(){
47116         this.items.each(function(f){
47117            f.clearInvalid();
47118         });
47119         
47120         Roo.each(this.childForms || [], function (f) {
47121             f.clearInvalid();
47122         });
47123         
47124         
47125         return this;
47126     },
47127
47128     /**
47129      * Resets this form.
47130      * @return {BasicForm} this
47131      */
47132     reset : function(){
47133         this.items.each(function(f){
47134             f.reset();
47135         });
47136         
47137         Roo.each(this.childForms || [], function (f) {
47138             f.reset();
47139         });
47140         this.resetHasChanged();
47141         
47142         return this;
47143     },
47144
47145     /**
47146      * Add Roo.form components to this form.
47147      * @param {Field} field1
47148      * @param {Field} field2 (optional)
47149      * @param {Field} etc (optional)
47150      * @return {BasicForm} this
47151      */
47152     add : function(){
47153         this.items.addAll(Array.prototype.slice.call(arguments, 0));
47154         return this;
47155     },
47156
47157
47158     /**
47159      * Removes a field from the items collection (does NOT remove its markup).
47160      * @param {Field} field
47161      * @return {BasicForm} this
47162      */
47163     remove : function(field){
47164         this.items.remove(field);
47165         return this;
47166     },
47167
47168     /**
47169      * Looks at the fields in this form, checks them for an id attribute,
47170      * and calls applyTo on the existing dom element with that id.
47171      * @return {BasicForm} this
47172      */
47173     render : function(){
47174         this.items.each(function(f){
47175             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
47176                 f.applyTo(f.id);
47177             }
47178         });
47179         return this;
47180     },
47181
47182     /**
47183      * Calls {@link Ext#apply} for all fields in this form with the passed object.
47184      * @param {Object} values
47185      * @return {BasicForm} this
47186      */
47187     applyToFields : function(o){
47188         this.items.each(function(f){
47189            Roo.apply(f, o);
47190         });
47191         return this;
47192     },
47193
47194     /**
47195      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
47196      * @param {Object} values
47197      * @return {BasicForm} this
47198      */
47199     applyIfToFields : function(o){
47200         this.items.each(function(f){
47201            Roo.applyIf(f, o);
47202         });
47203         return this;
47204     }
47205 });
47206
47207 // back compat
47208 Roo.BasicForm = Roo.form.BasicForm;/*
47209  * Based on:
47210  * Ext JS Library 1.1.1
47211  * Copyright(c) 2006-2007, Ext JS, LLC.
47212  *
47213  * Originally Released Under LGPL - original licence link has changed is not relivant.
47214  *
47215  * Fork - LGPL
47216  * <script type="text/javascript">
47217  */
47218
47219 /**
47220  * @class Roo.form.Form
47221  * @extends Roo.form.BasicForm
47222  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
47223  * @constructor
47224  * @param {Object} config Configuration options
47225  */
47226 Roo.form.Form = function(config){
47227     var xitems =  [];
47228     if (config.items) {
47229         xitems = config.items;
47230         delete config.items;
47231     }
47232    
47233     
47234     Roo.form.Form.superclass.constructor.call(this, null, config);
47235     this.url = this.url || this.action;
47236     if(!this.root){
47237         this.root = new Roo.form.Layout(Roo.applyIf({
47238             id: Roo.id()
47239         }, config));
47240     }
47241     this.active = this.root;
47242     /**
47243      * Array of all the buttons that have been added to this form via {@link addButton}
47244      * @type Array
47245      */
47246     this.buttons = [];
47247     this.allItems = [];
47248     this.addEvents({
47249         /**
47250          * @event clientvalidation
47251          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
47252          * @param {Form} this
47253          * @param {Boolean} valid true if the form has passed client-side validation
47254          */
47255         clientvalidation: true,
47256         /**
47257          * @event rendered
47258          * Fires when the form is rendered
47259          * @param {Roo.form.Form} form
47260          */
47261         rendered : true
47262     });
47263     
47264     if (this.progressUrl) {
47265             // push a hidden field onto the list of fields..
47266             this.addxtype( {
47267                     xns: Roo.form, 
47268                     xtype : 'Hidden', 
47269                     name : 'UPLOAD_IDENTIFIER' 
47270             });
47271         }
47272         
47273     
47274     Roo.each(xitems, this.addxtype, this);
47275     
47276     
47277     
47278 };
47279
47280 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
47281     /**
47282      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
47283      */
47284     /**
47285      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
47286      */
47287     /**
47288      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
47289      */
47290     buttonAlign:'center',
47291
47292     /**
47293      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
47294      */
47295     minButtonWidth:75,
47296
47297     /**
47298      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
47299      * This property cascades to child containers if not set.
47300      */
47301     labelAlign:'left',
47302
47303     /**
47304      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
47305      * fires a looping event with that state. This is required to bind buttons to the valid
47306      * state using the config value formBind:true on the button.
47307      */
47308     monitorValid : false,
47309
47310     /**
47311      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
47312      */
47313     monitorPoll : 200,
47314     
47315     /**
47316      * @cfg {String} progressUrl - Url to return progress data 
47317      */
47318     
47319     progressUrl : false,
47320   
47321     /**
47322      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
47323      * fields are added and the column is closed. If no fields are passed the column remains open
47324      * until end() is called.
47325      * @param {Object} config The config to pass to the column
47326      * @param {Field} field1 (optional)
47327      * @param {Field} field2 (optional)
47328      * @param {Field} etc (optional)
47329      * @return Column The column container object
47330      */
47331     column : function(c){
47332         var col = new Roo.form.Column(c);
47333         this.start(col);
47334         if(arguments.length > 1){ // duplicate code required because of Opera
47335             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47336             this.end();
47337         }
47338         return col;
47339     },
47340
47341     /**
47342      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
47343      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
47344      * until end() is called.
47345      * @param {Object} config The config to pass to the fieldset
47346      * @param {Field} field1 (optional)
47347      * @param {Field} field2 (optional)
47348      * @param {Field} etc (optional)
47349      * @return FieldSet The fieldset container object
47350      */
47351     fieldset : function(c){
47352         var fs = new Roo.form.FieldSet(c);
47353         this.start(fs);
47354         if(arguments.length > 1){ // duplicate code required because of Opera
47355             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47356             this.end();
47357         }
47358         return fs;
47359     },
47360
47361     /**
47362      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
47363      * fields are added and the container is closed. If no fields are passed the container remains open
47364      * until end() is called.
47365      * @param {Object} config The config to pass to the Layout
47366      * @param {Field} field1 (optional)
47367      * @param {Field} field2 (optional)
47368      * @param {Field} etc (optional)
47369      * @return Layout The container object
47370      */
47371     container : function(c){
47372         var l = new Roo.form.Layout(c);
47373         this.start(l);
47374         if(arguments.length > 1){ // duplicate code required because of Opera
47375             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47376             this.end();
47377         }
47378         return l;
47379     },
47380
47381     /**
47382      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
47383      * @param {Object} container A Roo.form.Layout or subclass of Layout
47384      * @return {Form} this
47385      */
47386     start : function(c){
47387         // cascade label info
47388         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
47389         this.active.stack.push(c);
47390         c.ownerCt = this.active;
47391         this.active = c;
47392         return this;
47393     },
47394
47395     /**
47396      * Closes the current open container
47397      * @return {Form} this
47398      */
47399     end : function(){
47400         if(this.active == this.root){
47401             return this;
47402         }
47403         this.active = this.active.ownerCt;
47404         return this;
47405     },
47406
47407     /**
47408      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
47409      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
47410      * as the label of the field.
47411      * @param {Field} field1
47412      * @param {Field} field2 (optional)
47413      * @param {Field} etc. (optional)
47414      * @return {Form} this
47415      */
47416     add : function(){
47417         this.active.stack.push.apply(this.active.stack, arguments);
47418         this.allItems.push.apply(this.allItems,arguments);
47419         var r = [];
47420         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
47421             if(a[i].isFormField){
47422                 r.push(a[i]);
47423             }
47424         }
47425         if(r.length > 0){
47426             Roo.form.Form.superclass.add.apply(this, r);
47427         }
47428         return this;
47429     },
47430     
47431
47432     
47433     
47434     
47435      /**
47436      * Find any element that has been added to a form, using it's ID or name
47437      * This can include framesets, columns etc. along with regular fields..
47438      * @param {String} id - id or name to find.
47439      
47440      * @return {Element} e - or false if nothing found.
47441      */
47442     findbyId : function(id)
47443     {
47444         var ret = false;
47445         if (!id) {
47446             return ret;
47447         }
47448         Roo.each(this.allItems, function(f){
47449             if (f.id == id || f.name == id ){
47450                 ret = f;
47451                 return false;
47452             }
47453         });
47454         return ret;
47455     },
47456
47457     
47458     
47459     /**
47460      * Render this form into the passed container. This should only be called once!
47461      * @param {String/HTMLElement/Element} container The element this component should be rendered into
47462      * @return {Form} this
47463      */
47464     render : function(ct)
47465     {
47466         
47467         
47468         
47469         ct = Roo.get(ct);
47470         var o = this.autoCreate || {
47471             tag: 'form',
47472             method : this.method || 'POST',
47473             id : this.id || Roo.id()
47474         };
47475         this.initEl(ct.createChild(o));
47476
47477         this.root.render(this.el);
47478         
47479        
47480              
47481         this.items.each(function(f){
47482             f.render('x-form-el-'+f.id);
47483         });
47484
47485         if(this.buttons.length > 0){
47486             // tables are required to maintain order and for correct IE layout
47487             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
47488                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
47489                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
47490             }}, null, true);
47491             var tr = tb.getElementsByTagName('tr')[0];
47492             for(var i = 0, len = this.buttons.length; i < len; i++) {
47493                 var b = this.buttons[i];
47494                 var td = document.createElement('td');
47495                 td.className = 'x-form-btn-td';
47496                 b.render(tr.appendChild(td));
47497             }
47498         }
47499         if(this.monitorValid){ // initialize after render
47500             this.startMonitoring();
47501         }
47502         this.fireEvent('rendered', this);
47503         return this;
47504     },
47505
47506     /**
47507      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
47508      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
47509      * object or a valid Roo.DomHelper element config
47510      * @param {Function} handler The function called when the button is clicked
47511      * @param {Object} scope (optional) The scope of the handler function
47512      * @return {Roo.Button}
47513      */
47514     addButton : function(config, handler, scope){
47515         var bc = {
47516             handler: handler,
47517             scope: scope,
47518             minWidth: this.minButtonWidth,
47519             hideParent:true
47520         };
47521         if(typeof config == "string"){
47522             bc.text = config;
47523         }else{
47524             Roo.apply(bc, config);
47525         }
47526         var btn = new Roo.Button(null, bc);
47527         this.buttons.push(btn);
47528         return btn;
47529     },
47530
47531      /**
47532      * Adds a series of form elements (using the xtype property as the factory method.
47533      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
47534      * @param {Object} config 
47535      */
47536     
47537     addxtype : function()
47538     {
47539         var ar = Array.prototype.slice.call(arguments, 0);
47540         var ret = false;
47541         for(var i = 0; i < ar.length; i++) {
47542             if (!ar[i]) {
47543                 continue; // skip -- if this happends something invalid got sent, we 
47544                 // should ignore it, as basically that interface element will not show up
47545                 // and that should be pretty obvious!!
47546             }
47547             
47548             if (Roo.form[ar[i].xtype]) {
47549                 ar[i].form = this;
47550                 var fe = Roo.factory(ar[i], Roo.form);
47551                 if (!ret) {
47552                     ret = fe;
47553                 }
47554                 fe.form = this;
47555                 if (fe.store) {
47556                     fe.store.form = this;
47557                 }
47558                 if (fe.isLayout) {  
47559                          
47560                     this.start(fe);
47561                     this.allItems.push(fe);
47562                     if (fe.items && fe.addxtype) {
47563                         fe.addxtype.apply(fe, fe.items);
47564                         delete fe.items;
47565                     }
47566                      this.end();
47567                     continue;
47568                 }
47569                 
47570                 
47571                  
47572                 this.add(fe);
47573               //  console.log('adding ' + ar[i].xtype);
47574             }
47575             if (ar[i].xtype == 'Button') {  
47576                 //console.log('adding button');
47577                 //console.log(ar[i]);
47578                 this.addButton(ar[i]);
47579                 this.allItems.push(fe);
47580                 continue;
47581             }
47582             
47583             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
47584                 alert('end is not supported on xtype any more, use items');
47585             //    this.end();
47586             //    //console.log('adding end');
47587             }
47588             
47589         }
47590         return ret;
47591     },
47592     
47593     /**
47594      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
47595      * option "monitorValid"
47596      */
47597     startMonitoring : function(){
47598         if(!this.bound){
47599             this.bound = true;
47600             Roo.TaskMgr.start({
47601                 run : this.bindHandler,
47602                 interval : this.monitorPoll || 200,
47603                 scope: this
47604             });
47605         }
47606     },
47607
47608     /**
47609      * Stops monitoring of the valid state of this form
47610      */
47611     stopMonitoring : function(){
47612         this.bound = false;
47613     },
47614
47615     // private
47616     bindHandler : function(){
47617         if(!this.bound){
47618             return false; // stops binding
47619         }
47620         var valid = true;
47621         this.items.each(function(f){
47622             if(!f.isValid(true)){
47623                 valid = false;
47624                 return false;
47625             }
47626         });
47627         for(var i = 0, len = this.buttons.length; i < len; i++){
47628             var btn = this.buttons[i];
47629             if(btn.formBind === true && btn.disabled === valid){
47630                 btn.setDisabled(!valid);
47631             }
47632         }
47633         this.fireEvent('clientvalidation', this, valid);
47634     }
47635     
47636     
47637     
47638     
47639     
47640     
47641     
47642     
47643 });
47644
47645
47646 // back compat
47647 Roo.Form = Roo.form.Form;
47648 /*
47649  * Based on:
47650  * Ext JS Library 1.1.1
47651  * Copyright(c) 2006-2007, Ext JS, LLC.
47652  *
47653  * Originally Released Under LGPL - original licence link has changed is not relivant.
47654  *
47655  * Fork - LGPL
47656  * <script type="text/javascript">
47657  */
47658
47659 // as we use this in bootstrap.
47660 Roo.namespace('Roo.form');
47661  /**
47662  * @class Roo.form.Action
47663  * Internal Class used to handle form actions
47664  * @constructor
47665  * @param {Roo.form.BasicForm} el The form element or its id
47666  * @param {Object} config Configuration options
47667  */
47668
47669  
47670  
47671 // define the action interface
47672 Roo.form.Action = function(form, options){
47673     this.form = form;
47674     this.options = options || {};
47675 };
47676 /**
47677  * Client Validation Failed
47678  * @const 
47679  */
47680 Roo.form.Action.CLIENT_INVALID = 'client';
47681 /**
47682  * Server Validation Failed
47683  * @const 
47684  */
47685 Roo.form.Action.SERVER_INVALID = 'server';
47686  /**
47687  * Connect to Server Failed
47688  * @const 
47689  */
47690 Roo.form.Action.CONNECT_FAILURE = 'connect';
47691 /**
47692  * Reading Data from Server Failed
47693  * @const 
47694  */
47695 Roo.form.Action.LOAD_FAILURE = 'load';
47696
47697 Roo.form.Action.prototype = {
47698     type : 'default',
47699     failureType : undefined,
47700     response : undefined,
47701     result : undefined,
47702
47703     // interface method
47704     run : function(options){
47705
47706     },
47707
47708     // interface method
47709     success : function(response){
47710
47711     },
47712
47713     // interface method
47714     handleResponse : function(response){
47715
47716     },
47717
47718     // default connection failure
47719     failure : function(response){
47720         
47721         this.response = response;
47722         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47723         this.form.afterAction(this, false);
47724     },
47725
47726     processResponse : function(response){
47727         this.response = response;
47728         if(!response.responseText){
47729             return true;
47730         }
47731         this.result = this.handleResponse(response);
47732         return this.result;
47733     },
47734
47735     // utility functions used internally
47736     getUrl : function(appendParams){
47737         var url = this.options.url || this.form.url || this.form.el.dom.action;
47738         if(appendParams){
47739             var p = this.getParams();
47740             if(p){
47741                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
47742             }
47743         }
47744         return url;
47745     },
47746
47747     getMethod : function(){
47748         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
47749     },
47750
47751     getParams : function(){
47752         var bp = this.form.baseParams;
47753         var p = this.options.params;
47754         if(p){
47755             if(typeof p == "object"){
47756                 p = Roo.urlEncode(Roo.applyIf(p, bp));
47757             }else if(typeof p == 'string' && bp){
47758                 p += '&' + Roo.urlEncode(bp);
47759             }
47760         }else if(bp){
47761             p = Roo.urlEncode(bp);
47762         }
47763         return p;
47764     },
47765
47766     createCallback : function(){
47767         return {
47768             success: this.success,
47769             failure: this.failure,
47770             scope: this,
47771             timeout: (this.form.timeout*1000),
47772             upload: this.form.fileUpload ? this.success : undefined
47773         };
47774     }
47775 };
47776
47777 Roo.form.Action.Submit = function(form, options){
47778     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
47779 };
47780
47781 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
47782     type : 'submit',
47783
47784     haveProgress : false,
47785     uploadComplete : false,
47786     
47787     // uploadProgress indicator.
47788     uploadProgress : function()
47789     {
47790         if (!this.form.progressUrl) {
47791             return;
47792         }
47793         
47794         if (!this.haveProgress) {
47795             Roo.MessageBox.progress("Uploading", "Uploading");
47796         }
47797         if (this.uploadComplete) {
47798            Roo.MessageBox.hide();
47799            return;
47800         }
47801         
47802         this.haveProgress = true;
47803    
47804         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
47805         
47806         var c = new Roo.data.Connection();
47807         c.request({
47808             url : this.form.progressUrl,
47809             params: {
47810                 id : uid
47811             },
47812             method: 'GET',
47813             success : function(req){
47814                //console.log(data);
47815                 var rdata = false;
47816                 var edata;
47817                 try  {
47818                    rdata = Roo.decode(req.responseText)
47819                 } catch (e) {
47820                     Roo.log("Invalid data from server..");
47821                     Roo.log(edata);
47822                     return;
47823                 }
47824                 if (!rdata || !rdata.success) {
47825                     Roo.log(rdata);
47826                     Roo.MessageBox.alert(Roo.encode(rdata));
47827                     return;
47828                 }
47829                 var data = rdata.data;
47830                 
47831                 if (this.uploadComplete) {
47832                    Roo.MessageBox.hide();
47833                    return;
47834                 }
47835                    
47836                 if (data){
47837                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
47838                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
47839                     );
47840                 }
47841                 this.uploadProgress.defer(2000,this);
47842             },
47843        
47844             failure: function(data) {
47845                 Roo.log('progress url failed ');
47846                 Roo.log(data);
47847             },
47848             scope : this
47849         });
47850            
47851     },
47852     
47853     
47854     run : function()
47855     {
47856         // run get Values on the form, so it syncs any secondary forms.
47857         this.form.getValues();
47858         
47859         var o = this.options;
47860         var method = this.getMethod();
47861         var isPost = method == 'POST';
47862         if(o.clientValidation === false || this.form.isValid()){
47863             
47864             if (this.form.progressUrl) {
47865                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
47866                     (new Date() * 1) + '' + Math.random());
47867                     
47868             } 
47869             
47870             
47871             Roo.Ajax.request(Roo.apply(this.createCallback(), {
47872                 form:this.form.el.dom,
47873                 url:this.getUrl(!isPost),
47874                 method: method,
47875                 params:isPost ? this.getParams() : null,
47876                 isUpload: this.form.fileUpload
47877             }));
47878             
47879             this.uploadProgress();
47880
47881         }else if (o.clientValidation !== false){ // client validation failed
47882             this.failureType = Roo.form.Action.CLIENT_INVALID;
47883             this.form.afterAction(this, false);
47884         }
47885     },
47886
47887     success : function(response)
47888     {
47889         this.uploadComplete= true;
47890         if (this.haveProgress) {
47891             Roo.MessageBox.hide();
47892         }
47893         
47894         
47895         var result = this.processResponse(response);
47896         if(result === true || result.success){
47897             this.form.afterAction(this, true);
47898             return;
47899         }
47900         if(result.errors){
47901             this.form.markInvalid(result.errors);
47902             this.failureType = Roo.form.Action.SERVER_INVALID;
47903         }
47904         this.form.afterAction(this, false);
47905     },
47906     failure : function(response)
47907     {
47908         this.uploadComplete= true;
47909         if (this.haveProgress) {
47910             Roo.MessageBox.hide();
47911         }
47912         
47913         this.response = response;
47914         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47915         this.form.afterAction(this, false);
47916     },
47917     
47918     handleResponse : function(response){
47919         if(this.form.errorReader){
47920             var rs = this.form.errorReader.read(response);
47921             var errors = [];
47922             if(rs.records){
47923                 for(var i = 0, len = rs.records.length; i < len; i++) {
47924                     var r = rs.records[i];
47925                     errors[i] = r.data;
47926                 }
47927             }
47928             if(errors.length < 1){
47929                 errors = null;
47930             }
47931             return {
47932                 success : rs.success,
47933                 errors : errors
47934             };
47935         }
47936         var ret = false;
47937         try {
47938             ret = Roo.decode(response.responseText);
47939         } catch (e) {
47940             ret = {
47941                 success: false,
47942                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
47943                 errors : []
47944             };
47945         }
47946         return ret;
47947         
47948     }
47949 });
47950
47951
47952 Roo.form.Action.Load = function(form, options){
47953     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
47954     this.reader = this.form.reader;
47955 };
47956
47957 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
47958     type : 'load',
47959
47960     run : function(){
47961         
47962         Roo.Ajax.request(Roo.apply(
47963                 this.createCallback(), {
47964                     method:this.getMethod(),
47965                     url:this.getUrl(false),
47966                     params:this.getParams()
47967         }));
47968     },
47969
47970     success : function(response){
47971         
47972         var result = this.processResponse(response);
47973         if(result === true || !result.success || !result.data){
47974             this.failureType = Roo.form.Action.LOAD_FAILURE;
47975             this.form.afterAction(this, false);
47976             return;
47977         }
47978         this.form.clearInvalid();
47979         this.form.setValues(result.data);
47980         this.form.afterAction(this, true);
47981     },
47982
47983     handleResponse : function(response){
47984         if(this.form.reader){
47985             var rs = this.form.reader.read(response);
47986             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
47987             return {
47988                 success : rs.success,
47989                 data : data
47990             };
47991         }
47992         return Roo.decode(response.responseText);
47993     }
47994 });
47995
47996 Roo.form.Action.ACTION_TYPES = {
47997     'load' : Roo.form.Action.Load,
47998     'submit' : Roo.form.Action.Submit
47999 };/*
48000  * Based on:
48001  * Ext JS Library 1.1.1
48002  * Copyright(c) 2006-2007, Ext JS, LLC.
48003  *
48004  * Originally Released Under LGPL - original licence link has changed is not relivant.
48005  *
48006  * Fork - LGPL
48007  * <script type="text/javascript">
48008  */
48009  
48010 /**
48011  * @class Roo.form.Layout
48012  * @extends Roo.Component
48013  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
48014  * @constructor
48015  * @param {Object} config Configuration options
48016  */
48017 Roo.form.Layout = function(config){
48018     var xitems = [];
48019     if (config.items) {
48020         xitems = config.items;
48021         delete config.items;
48022     }
48023     Roo.form.Layout.superclass.constructor.call(this, config);
48024     this.stack = [];
48025     Roo.each(xitems, this.addxtype, this);
48026      
48027 };
48028
48029 Roo.extend(Roo.form.Layout, Roo.Component, {
48030     /**
48031      * @cfg {String/Object} autoCreate
48032      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
48033      */
48034     /**
48035      * @cfg {String/Object/Function} style
48036      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
48037      * a function which returns such a specification.
48038      */
48039     /**
48040      * @cfg {String} labelAlign
48041      * Valid values are "left," "top" and "right" (defaults to "left")
48042      */
48043     /**
48044      * @cfg {Number} labelWidth
48045      * Fixed width in pixels of all field labels (defaults to undefined)
48046      */
48047     /**
48048      * @cfg {Boolean} clear
48049      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
48050      */
48051     clear : true,
48052     /**
48053      * @cfg {String} labelSeparator
48054      * The separator to use after field labels (defaults to ':')
48055      */
48056     labelSeparator : ':',
48057     /**
48058      * @cfg {Boolean} hideLabels
48059      * True to suppress the display of field labels in this layout (defaults to false)
48060      */
48061     hideLabels : false,
48062
48063     // private
48064     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
48065     
48066     isLayout : true,
48067     
48068     // private
48069     onRender : function(ct, position){
48070         if(this.el){ // from markup
48071             this.el = Roo.get(this.el);
48072         }else {  // generate
48073             var cfg = this.getAutoCreate();
48074             this.el = ct.createChild(cfg, position);
48075         }
48076         if(this.style){
48077             this.el.applyStyles(this.style);
48078         }
48079         if(this.labelAlign){
48080             this.el.addClass('x-form-label-'+this.labelAlign);
48081         }
48082         if(this.hideLabels){
48083             this.labelStyle = "display:none";
48084             this.elementStyle = "padding-left:0;";
48085         }else{
48086             if(typeof this.labelWidth == 'number'){
48087                 this.labelStyle = "width:"+this.labelWidth+"px;";
48088                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
48089             }
48090             if(this.labelAlign == 'top'){
48091                 this.labelStyle = "width:auto;";
48092                 this.elementStyle = "padding-left:0;";
48093             }
48094         }
48095         var stack = this.stack;
48096         var slen = stack.length;
48097         if(slen > 0){
48098             if(!this.fieldTpl){
48099                 var t = new Roo.Template(
48100                     '<div class="x-form-item {5}">',
48101                         '<label for="{0}" style="{2}">{1}{4}</label>',
48102                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48103                         '</div>',
48104                     '</div><div class="x-form-clear-left"></div>'
48105                 );
48106                 t.disableFormats = true;
48107                 t.compile();
48108                 Roo.form.Layout.prototype.fieldTpl = t;
48109             }
48110             for(var i = 0; i < slen; i++) {
48111                 if(stack[i].isFormField){
48112                     this.renderField(stack[i]);
48113                 }else{
48114                     this.renderComponent(stack[i]);
48115                 }
48116             }
48117         }
48118         if(this.clear){
48119             this.el.createChild({cls:'x-form-clear'});
48120         }
48121     },
48122
48123     // private
48124     renderField : function(f){
48125         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
48126                f.id, //0
48127                f.fieldLabel, //1
48128                f.labelStyle||this.labelStyle||'', //2
48129                this.elementStyle||'', //3
48130                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
48131                f.itemCls||this.itemCls||''  //5
48132        ], true).getPrevSibling());
48133     },
48134
48135     // private
48136     renderComponent : function(c){
48137         c.render(c.isLayout ? this.el : this.el.createChild());    
48138     },
48139     /**
48140      * Adds a object form elements (using the xtype property as the factory method.)
48141      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
48142      * @param {Object} config 
48143      */
48144     addxtype : function(o)
48145     {
48146         // create the lement.
48147         o.form = this.form;
48148         var fe = Roo.factory(o, Roo.form);
48149         this.form.allItems.push(fe);
48150         this.stack.push(fe);
48151         
48152         if (fe.isFormField) {
48153             this.form.items.add(fe);
48154         }
48155          
48156         return fe;
48157     }
48158 });
48159
48160 /**
48161  * @class Roo.form.Column
48162  * @extends Roo.form.Layout
48163  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
48164  * @constructor
48165  * @param {Object} config Configuration options
48166  */
48167 Roo.form.Column = function(config){
48168     Roo.form.Column.superclass.constructor.call(this, config);
48169 };
48170
48171 Roo.extend(Roo.form.Column, Roo.form.Layout, {
48172     /**
48173      * @cfg {Number/String} width
48174      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48175      */
48176     /**
48177      * @cfg {String/Object} autoCreate
48178      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
48179      */
48180
48181     // private
48182     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
48183
48184     // private
48185     onRender : function(ct, position){
48186         Roo.form.Column.superclass.onRender.call(this, ct, position);
48187         if(this.width){
48188             this.el.setWidth(this.width);
48189         }
48190     }
48191 });
48192
48193
48194 /**
48195  * @class Roo.form.Row
48196  * @extends Roo.form.Layout
48197  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
48198  * @constructor
48199  * @param {Object} config Configuration options
48200  */
48201
48202  
48203 Roo.form.Row = function(config){
48204     Roo.form.Row.superclass.constructor.call(this, config);
48205 };
48206  
48207 Roo.extend(Roo.form.Row, Roo.form.Layout, {
48208       /**
48209      * @cfg {Number/String} width
48210      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48211      */
48212     /**
48213      * @cfg {Number/String} height
48214      * The fixed height of the column in pixels or CSS value (defaults to "auto")
48215      */
48216     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
48217     
48218     padWidth : 20,
48219     // private
48220     onRender : function(ct, position){
48221         //console.log('row render');
48222         if(!this.rowTpl){
48223             var t = new Roo.Template(
48224                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
48225                     '<label for="{0}" style="{2}">{1}{4}</label>',
48226                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48227                     '</div>',
48228                 '</div>'
48229             );
48230             t.disableFormats = true;
48231             t.compile();
48232             Roo.form.Layout.prototype.rowTpl = t;
48233         }
48234         this.fieldTpl = this.rowTpl;
48235         
48236         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
48237         var labelWidth = 100;
48238         
48239         if ((this.labelAlign != 'top')) {
48240             if (typeof this.labelWidth == 'number') {
48241                 labelWidth = this.labelWidth
48242             }
48243             this.padWidth =  20 + labelWidth;
48244             
48245         }
48246         
48247         Roo.form.Column.superclass.onRender.call(this, ct, position);
48248         if(this.width){
48249             this.el.setWidth(this.width);
48250         }
48251         if(this.height){
48252             this.el.setHeight(this.height);
48253         }
48254     },
48255     
48256     // private
48257     renderField : function(f){
48258         f.fieldEl = this.fieldTpl.append(this.el, [
48259                f.id, f.fieldLabel,
48260                f.labelStyle||this.labelStyle||'',
48261                this.elementStyle||'',
48262                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
48263                f.itemCls||this.itemCls||'',
48264                f.width ? f.width + this.padWidth : 160 + this.padWidth
48265        ],true);
48266     }
48267 });
48268  
48269
48270 /**
48271  * @class Roo.form.FieldSet
48272  * @extends Roo.form.Layout
48273  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
48274  * @constructor
48275  * @param {Object} config Configuration options
48276  */
48277 Roo.form.FieldSet = function(config){
48278     Roo.form.FieldSet.superclass.constructor.call(this, config);
48279 };
48280
48281 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
48282     /**
48283      * @cfg {String} legend
48284      * The text to display as the legend for the FieldSet (defaults to '')
48285      */
48286     /**
48287      * @cfg {String/Object} autoCreate
48288      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
48289      */
48290
48291     // private
48292     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
48293
48294     // private
48295     onRender : function(ct, position){
48296         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
48297         if(this.legend){
48298             this.setLegend(this.legend);
48299         }
48300     },
48301
48302     // private
48303     setLegend : function(text){
48304         if(this.rendered){
48305             this.el.child('legend').update(text);
48306         }
48307     }
48308 });/*
48309  * Based on:
48310  * Ext JS Library 1.1.1
48311  * Copyright(c) 2006-2007, Ext JS, LLC.
48312  *
48313  * Originally Released Under LGPL - original licence link has changed is not relivant.
48314  *
48315  * Fork - LGPL
48316  * <script type="text/javascript">
48317  */
48318 /**
48319  * @class Roo.form.VTypes
48320  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
48321  * @singleton
48322  */
48323 Roo.form.VTypes = function(){
48324     // closure these in so they are only created once.
48325     var alpha = /^[a-zA-Z_]+$/;
48326     var alphanum = /^[a-zA-Z0-9_]+$/;
48327     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
48328     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
48329
48330     // All these messages and functions are configurable
48331     return {
48332         /**
48333          * The function used to validate email addresses
48334          * @param {String} value The email address
48335          */
48336         'email' : function(v){
48337             return email.test(v);
48338         },
48339         /**
48340          * The error text to display when the email validation function returns false
48341          * @type String
48342          */
48343         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
48344         /**
48345          * The keystroke filter mask to be applied on email input
48346          * @type RegExp
48347          */
48348         'emailMask' : /[a-z0-9_\.\-@]/i,
48349
48350         /**
48351          * The function used to validate URLs
48352          * @param {String} value The URL
48353          */
48354         'url' : function(v){
48355             return url.test(v);
48356         },
48357         /**
48358          * The error text to display when the url validation function returns false
48359          * @type String
48360          */
48361         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
48362         
48363         /**
48364          * The function used to validate alpha values
48365          * @param {String} value The value
48366          */
48367         'alpha' : function(v){
48368             return alpha.test(v);
48369         },
48370         /**
48371          * The error text to display when the alpha validation function returns false
48372          * @type String
48373          */
48374         'alphaText' : 'This field should only contain letters and _',
48375         /**
48376          * The keystroke filter mask to be applied on alpha input
48377          * @type RegExp
48378          */
48379         'alphaMask' : /[a-z_]/i,
48380
48381         /**
48382          * The function used to validate alphanumeric values
48383          * @param {String} value The value
48384          */
48385         'alphanum' : function(v){
48386             return alphanum.test(v);
48387         },
48388         /**
48389          * The error text to display when the alphanumeric validation function returns false
48390          * @type String
48391          */
48392         'alphanumText' : 'This field should only contain letters, numbers and _',
48393         /**
48394          * The keystroke filter mask to be applied on alphanumeric input
48395          * @type RegExp
48396          */
48397         'alphanumMask' : /[a-z0-9_]/i
48398     };
48399 }();//<script type="text/javascript">
48400
48401 /**
48402  * @class Roo.form.FCKeditor
48403  * @extends Roo.form.TextArea
48404  * Wrapper around the FCKEditor http://www.fckeditor.net
48405  * @constructor
48406  * Creates a new FCKeditor
48407  * @param {Object} config Configuration options
48408  */
48409 Roo.form.FCKeditor = function(config){
48410     Roo.form.FCKeditor.superclass.constructor.call(this, config);
48411     this.addEvents({
48412          /**
48413          * @event editorinit
48414          * Fired when the editor is initialized - you can add extra handlers here..
48415          * @param {FCKeditor} this
48416          * @param {Object} the FCK object.
48417          */
48418         editorinit : true
48419     });
48420     
48421     
48422 };
48423 Roo.form.FCKeditor.editors = { };
48424 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
48425 {
48426     //defaultAutoCreate : {
48427     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
48428     //},
48429     // private
48430     /**
48431      * @cfg {Object} fck options - see fck manual for details.
48432      */
48433     fckconfig : false,
48434     
48435     /**
48436      * @cfg {Object} fck toolbar set (Basic or Default)
48437      */
48438     toolbarSet : 'Basic',
48439     /**
48440      * @cfg {Object} fck BasePath
48441      */ 
48442     basePath : '/fckeditor/',
48443     
48444     
48445     frame : false,
48446     
48447     value : '',
48448     
48449    
48450     onRender : function(ct, position)
48451     {
48452         if(!this.el){
48453             this.defaultAutoCreate = {
48454                 tag: "textarea",
48455                 style:"width:300px;height:60px;",
48456                 autocomplete: "new-password"
48457             };
48458         }
48459         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
48460         /*
48461         if(this.grow){
48462             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
48463             if(this.preventScrollbars){
48464                 this.el.setStyle("overflow", "hidden");
48465             }
48466             this.el.setHeight(this.growMin);
48467         }
48468         */
48469         //console.log('onrender' + this.getId() );
48470         Roo.form.FCKeditor.editors[this.getId()] = this;
48471          
48472
48473         this.replaceTextarea() ;
48474         
48475     },
48476     
48477     getEditor : function() {
48478         return this.fckEditor;
48479     },
48480     /**
48481      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
48482      * @param {Mixed} value The value to set
48483      */
48484     
48485     
48486     setValue : function(value)
48487     {
48488         //console.log('setValue: ' + value);
48489         
48490         if(typeof(value) == 'undefined') { // not sure why this is happending...
48491             return;
48492         }
48493         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48494         
48495         //if(!this.el || !this.getEditor()) {
48496         //    this.value = value;
48497             //this.setValue.defer(100,this,[value]);    
48498         //    return;
48499         //} 
48500         
48501         if(!this.getEditor()) {
48502             return;
48503         }
48504         
48505         this.getEditor().SetData(value);
48506         
48507         //
48508
48509     },
48510
48511     /**
48512      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
48513      * @return {Mixed} value The field value
48514      */
48515     getValue : function()
48516     {
48517         
48518         if (this.frame && this.frame.dom.style.display == 'none') {
48519             return Roo.form.FCKeditor.superclass.getValue.call(this);
48520         }
48521         
48522         if(!this.el || !this.getEditor()) {
48523            
48524            // this.getValue.defer(100,this); 
48525             return this.value;
48526         }
48527        
48528         
48529         var value=this.getEditor().GetData();
48530         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48531         return Roo.form.FCKeditor.superclass.getValue.call(this);
48532         
48533
48534     },
48535
48536     /**
48537      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
48538      * @return {Mixed} value The field value
48539      */
48540     getRawValue : function()
48541     {
48542         if (this.frame && this.frame.dom.style.display == 'none') {
48543             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48544         }
48545         
48546         if(!this.el || !this.getEditor()) {
48547             //this.getRawValue.defer(100,this); 
48548             return this.value;
48549             return;
48550         }
48551         
48552         
48553         
48554         var value=this.getEditor().GetData();
48555         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
48556         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48557          
48558     },
48559     
48560     setSize : function(w,h) {
48561         
48562         
48563         
48564         //if (this.frame && this.frame.dom.style.display == 'none') {
48565         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48566         //    return;
48567         //}
48568         //if(!this.el || !this.getEditor()) {
48569         //    this.setSize.defer(100,this, [w,h]); 
48570         //    return;
48571         //}
48572         
48573         
48574         
48575         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48576         
48577         this.frame.dom.setAttribute('width', w);
48578         this.frame.dom.setAttribute('height', h);
48579         this.frame.setSize(w,h);
48580         
48581     },
48582     
48583     toggleSourceEdit : function(value) {
48584         
48585       
48586          
48587         this.el.dom.style.display = value ? '' : 'none';
48588         this.frame.dom.style.display = value ?  'none' : '';
48589         
48590     },
48591     
48592     
48593     focus: function(tag)
48594     {
48595         if (this.frame.dom.style.display == 'none') {
48596             return Roo.form.FCKeditor.superclass.focus.call(this);
48597         }
48598         if(!this.el || !this.getEditor()) {
48599             this.focus.defer(100,this, [tag]); 
48600             return;
48601         }
48602         
48603         
48604         
48605         
48606         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
48607         this.getEditor().Focus();
48608         if (tgs.length) {
48609             if (!this.getEditor().Selection.GetSelection()) {
48610                 this.focus.defer(100,this, [tag]); 
48611                 return;
48612             }
48613             
48614             
48615             var r = this.getEditor().EditorDocument.createRange();
48616             r.setStart(tgs[0],0);
48617             r.setEnd(tgs[0],0);
48618             this.getEditor().Selection.GetSelection().removeAllRanges();
48619             this.getEditor().Selection.GetSelection().addRange(r);
48620             this.getEditor().Focus();
48621         }
48622         
48623     },
48624     
48625     
48626     
48627     replaceTextarea : function()
48628     {
48629         if ( document.getElementById( this.getId() + '___Frame' ) ) {
48630             return ;
48631         }
48632         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
48633         //{
48634             // We must check the elements firstly using the Id and then the name.
48635         var oTextarea = document.getElementById( this.getId() );
48636         
48637         var colElementsByName = document.getElementsByName( this.getId() ) ;
48638          
48639         oTextarea.style.display = 'none' ;
48640
48641         if ( oTextarea.tabIndex ) {            
48642             this.TabIndex = oTextarea.tabIndex ;
48643         }
48644         
48645         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
48646         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
48647         this.frame = Roo.get(this.getId() + '___Frame')
48648     },
48649     
48650     _getConfigHtml : function()
48651     {
48652         var sConfig = '' ;
48653
48654         for ( var o in this.fckconfig ) {
48655             sConfig += sConfig.length > 0  ? '&amp;' : '';
48656             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
48657         }
48658
48659         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
48660     },
48661     
48662     
48663     _getIFrameHtml : function()
48664     {
48665         var sFile = 'fckeditor.html' ;
48666         /* no idea what this is about..
48667         try
48668         {
48669             if ( (/fcksource=true/i).test( window.top.location.search ) )
48670                 sFile = 'fckeditor.original.html' ;
48671         }
48672         catch (e) { 
48673         */
48674
48675         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
48676         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
48677         
48678         
48679         var html = '<iframe id="' + this.getId() +
48680             '___Frame" src="' + sLink +
48681             '" width="' + this.width +
48682             '" height="' + this.height + '"' +
48683             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
48684             ' frameborder="0" scrolling="no"></iframe>' ;
48685
48686         return html ;
48687     },
48688     
48689     _insertHtmlBefore : function( html, element )
48690     {
48691         if ( element.insertAdjacentHTML )       {
48692             // IE
48693             element.insertAdjacentHTML( 'beforeBegin', html ) ;
48694         } else { // Gecko
48695             var oRange = document.createRange() ;
48696             oRange.setStartBefore( element ) ;
48697             var oFragment = oRange.createContextualFragment( html );
48698             element.parentNode.insertBefore( oFragment, element ) ;
48699         }
48700     }
48701     
48702     
48703   
48704     
48705     
48706     
48707     
48708
48709 });
48710
48711 //Roo.reg('fckeditor', Roo.form.FCKeditor);
48712
48713 function FCKeditor_OnComplete(editorInstance){
48714     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
48715     f.fckEditor = editorInstance;
48716     //console.log("loaded");
48717     f.fireEvent('editorinit', f, editorInstance);
48718
48719   
48720
48721  
48722
48723
48724
48725
48726
48727
48728
48729
48730
48731
48732
48733
48734
48735
48736
48737 //<script type="text/javascript">
48738 /**
48739  * @class Roo.form.GridField
48740  * @extends Roo.form.Field
48741  * Embed a grid (or editable grid into a form)
48742  * STATUS ALPHA
48743  * 
48744  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
48745  * it needs 
48746  * xgrid.store = Roo.data.Store
48747  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
48748  * xgrid.store.reader = Roo.data.JsonReader 
48749  * 
48750  * 
48751  * @constructor
48752  * Creates a new GridField
48753  * @param {Object} config Configuration options
48754  */
48755 Roo.form.GridField = function(config){
48756     Roo.form.GridField.superclass.constructor.call(this, config);
48757      
48758 };
48759
48760 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
48761     /**
48762      * @cfg {Number} width  - used to restrict width of grid..
48763      */
48764     width : 100,
48765     /**
48766      * @cfg {Number} height - used to restrict height of grid..
48767      */
48768     height : 50,
48769      /**
48770      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
48771          * 
48772          *}
48773      */
48774     xgrid : false, 
48775     /**
48776      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48777      * {tag: "input", type: "checkbox", autocomplete: "off"})
48778      */
48779    // defaultAutoCreate : { tag: 'div' },
48780     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
48781     /**
48782      * @cfg {String} addTitle Text to include for adding a title.
48783      */
48784     addTitle : false,
48785     //
48786     onResize : function(){
48787         Roo.form.Field.superclass.onResize.apply(this, arguments);
48788     },
48789
48790     initEvents : function(){
48791         // Roo.form.Checkbox.superclass.initEvents.call(this);
48792         // has no events...
48793        
48794     },
48795
48796
48797     getResizeEl : function(){
48798         return this.wrap;
48799     },
48800
48801     getPositionEl : function(){
48802         return this.wrap;
48803     },
48804
48805     // private
48806     onRender : function(ct, position){
48807         
48808         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
48809         var style = this.style;
48810         delete this.style;
48811         
48812         Roo.form.GridField.superclass.onRender.call(this, ct, position);
48813         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
48814         this.viewEl = this.wrap.createChild({ tag: 'div' });
48815         if (style) {
48816             this.viewEl.applyStyles(style);
48817         }
48818         if (this.width) {
48819             this.viewEl.setWidth(this.width);
48820         }
48821         if (this.height) {
48822             this.viewEl.setHeight(this.height);
48823         }
48824         //if(this.inputValue !== undefined){
48825         //this.setValue(this.value);
48826         
48827         
48828         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
48829         
48830         
48831         this.grid.render();
48832         this.grid.getDataSource().on('remove', this.refreshValue, this);
48833         this.grid.getDataSource().on('update', this.refreshValue, this);
48834         this.grid.on('afteredit', this.refreshValue, this);
48835  
48836     },
48837      
48838     
48839     /**
48840      * Sets the value of the item. 
48841      * @param {String} either an object  or a string..
48842      */
48843     setValue : function(v){
48844         //this.value = v;
48845         v = v || []; // empty set..
48846         // this does not seem smart - it really only affects memoryproxy grids..
48847         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
48848             var ds = this.grid.getDataSource();
48849             // assumes a json reader..
48850             var data = {}
48851             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
48852             ds.loadData( data);
48853         }
48854         // clear selection so it does not get stale.
48855         if (this.grid.sm) { 
48856             this.grid.sm.clearSelections();
48857         }
48858         
48859         Roo.form.GridField.superclass.setValue.call(this, v);
48860         this.refreshValue();
48861         // should load data in the grid really....
48862     },
48863     
48864     // private
48865     refreshValue: function() {
48866          var val = [];
48867         this.grid.getDataSource().each(function(r) {
48868             val.push(r.data);
48869         });
48870         this.el.dom.value = Roo.encode(val);
48871     }
48872     
48873      
48874     
48875     
48876 });/*
48877  * Based on:
48878  * Ext JS Library 1.1.1
48879  * Copyright(c) 2006-2007, Ext JS, LLC.
48880  *
48881  * Originally Released Under LGPL - original licence link has changed is not relivant.
48882  *
48883  * Fork - LGPL
48884  * <script type="text/javascript">
48885  */
48886 /**
48887  * @class Roo.form.DisplayField
48888  * @extends Roo.form.Field
48889  * A generic Field to display non-editable data.
48890  * @cfg {Boolean} closable (true|false) default false
48891  * @constructor
48892  * Creates a new Display Field item.
48893  * @param {Object} config Configuration options
48894  */
48895 Roo.form.DisplayField = function(config){
48896     Roo.form.DisplayField.superclass.constructor.call(this, config);
48897     
48898     this.addEvents({
48899         /**
48900          * @event close
48901          * Fires after the click the close btn
48902              * @param {Roo.form.DisplayField} this
48903              */
48904         close : true
48905     });
48906 };
48907
48908 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
48909     inputType:      'hidden',
48910     allowBlank:     true,
48911     readOnly:         true,
48912     
48913  
48914     /**
48915      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
48916      */
48917     focusClass : undefined,
48918     /**
48919      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
48920      */
48921     fieldClass: 'x-form-field',
48922     
48923      /**
48924      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
48925      */
48926     valueRenderer: undefined,
48927     
48928     width: 100,
48929     /**
48930      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48931      * {tag: "input", type: "checkbox", autocomplete: "off"})
48932      */
48933      
48934  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
48935  
48936     closable : false,
48937     
48938     onResize : function(){
48939         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
48940         
48941     },
48942
48943     initEvents : function(){
48944         // Roo.form.Checkbox.superclass.initEvents.call(this);
48945         // has no events...
48946         
48947         if(this.closable){
48948             this.closeEl.on('click', this.onClose, this);
48949         }
48950        
48951     },
48952
48953
48954     getResizeEl : function(){
48955         return this.wrap;
48956     },
48957
48958     getPositionEl : function(){
48959         return this.wrap;
48960     },
48961
48962     // private
48963     onRender : function(ct, position){
48964         
48965         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
48966         //if(this.inputValue !== undefined){
48967         this.wrap = this.el.wrap();
48968         
48969         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
48970         
48971         if(this.closable){
48972             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
48973         }
48974         
48975         if (this.bodyStyle) {
48976             this.viewEl.applyStyles(this.bodyStyle);
48977         }
48978         //this.viewEl.setStyle('padding', '2px');
48979         
48980         this.setValue(this.value);
48981         
48982     },
48983 /*
48984     // private
48985     initValue : Roo.emptyFn,
48986
48987   */
48988
48989         // private
48990     onClick : function(){
48991         
48992     },
48993
48994     /**
48995      * Sets the checked state of the checkbox.
48996      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
48997      */
48998     setValue : function(v){
48999         this.value = v;
49000         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
49001         // this might be called before we have a dom element..
49002         if (!this.viewEl) {
49003             return;
49004         }
49005         this.viewEl.dom.innerHTML = html;
49006         Roo.form.DisplayField.superclass.setValue.call(this, v);
49007
49008     },
49009     
49010     onClose : function(e)
49011     {
49012         e.preventDefault();
49013         
49014         this.fireEvent('close', this);
49015     }
49016 });/*
49017  * 
49018  * Licence- LGPL
49019  * 
49020  */
49021
49022 /**
49023  * @class Roo.form.DayPicker
49024  * @extends Roo.form.Field
49025  * A Day picker show [M] [T] [W] ....
49026  * @constructor
49027  * Creates a new Day Picker
49028  * @param {Object} config Configuration options
49029  */
49030 Roo.form.DayPicker= function(config){
49031     Roo.form.DayPicker.superclass.constructor.call(this, config);
49032      
49033 };
49034
49035 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
49036     /**
49037      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49038      */
49039     focusClass : undefined,
49040     /**
49041      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49042      */
49043     fieldClass: "x-form-field",
49044    
49045     /**
49046      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49047      * {tag: "input", type: "checkbox", autocomplete: "off"})
49048      */
49049     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
49050     
49051    
49052     actionMode : 'viewEl', 
49053     //
49054     // private
49055  
49056     inputType : 'hidden',
49057     
49058      
49059     inputElement: false, // real input element?
49060     basedOn: false, // ????
49061     
49062     isFormField: true, // not sure where this is needed!!!!
49063
49064     onResize : function(){
49065         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
49066         if(!this.boxLabel){
49067             this.el.alignTo(this.wrap, 'c-c');
49068         }
49069     },
49070
49071     initEvents : function(){
49072         Roo.form.Checkbox.superclass.initEvents.call(this);
49073         this.el.on("click", this.onClick,  this);
49074         this.el.on("change", this.onClick,  this);
49075     },
49076
49077
49078     getResizeEl : function(){
49079         return this.wrap;
49080     },
49081
49082     getPositionEl : function(){
49083         return this.wrap;
49084     },
49085
49086     
49087     // private
49088     onRender : function(ct, position){
49089         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
49090        
49091         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
49092         
49093         var r1 = '<table><tr>';
49094         var r2 = '<tr class="x-form-daypick-icons">';
49095         for (var i=0; i < 7; i++) {
49096             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
49097             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
49098         }
49099         
49100         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
49101         viewEl.select('img').on('click', this.onClick, this);
49102         this.viewEl = viewEl;   
49103         
49104         
49105         // this will not work on Chrome!!!
49106         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
49107         this.el.on('propertychange', this.setFromHidden,  this);  //ie
49108         
49109         
49110           
49111
49112     },
49113
49114     // private
49115     initValue : Roo.emptyFn,
49116
49117     /**
49118      * Returns the checked state of the checkbox.
49119      * @return {Boolean} True if checked, else false
49120      */
49121     getValue : function(){
49122         return this.el.dom.value;
49123         
49124     },
49125
49126         // private
49127     onClick : function(e){ 
49128         //this.setChecked(!this.checked);
49129         Roo.get(e.target).toggleClass('x-menu-item-checked');
49130         this.refreshValue();
49131         //if(this.el.dom.checked != this.checked){
49132         //    this.setValue(this.el.dom.checked);
49133        // }
49134     },
49135     
49136     // private
49137     refreshValue : function()
49138     {
49139         var val = '';
49140         this.viewEl.select('img',true).each(function(e,i,n)  {
49141             val += e.is(".x-menu-item-checked") ? String(n) : '';
49142         });
49143         this.setValue(val, true);
49144     },
49145
49146     /**
49147      * Sets the checked state of the checkbox.
49148      * On is always based on a string comparison between inputValue and the param.
49149      * @param {Boolean/String} value - the value to set 
49150      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
49151      */
49152     setValue : function(v,suppressEvent){
49153         if (!this.el.dom) {
49154             return;
49155         }
49156         var old = this.el.dom.value ;
49157         this.el.dom.value = v;
49158         if (suppressEvent) {
49159             return ;
49160         }
49161          
49162         // update display..
49163         this.viewEl.select('img',true).each(function(e,i,n)  {
49164             
49165             var on = e.is(".x-menu-item-checked");
49166             var newv = v.indexOf(String(n)) > -1;
49167             if (on != newv) {
49168                 e.toggleClass('x-menu-item-checked');
49169             }
49170             
49171         });
49172         
49173         
49174         this.fireEvent('change', this, v, old);
49175         
49176         
49177     },
49178    
49179     // handle setting of hidden value by some other method!!?!?
49180     setFromHidden: function()
49181     {
49182         if(!this.el){
49183             return;
49184         }
49185         //console.log("SET FROM HIDDEN");
49186         //alert('setFrom hidden');
49187         this.setValue(this.el.dom.value);
49188     },
49189     
49190     onDestroy : function()
49191     {
49192         if(this.viewEl){
49193             Roo.get(this.viewEl).remove();
49194         }
49195          
49196         Roo.form.DayPicker.superclass.onDestroy.call(this);
49197     }
49198
49199 });/*
49200  * RooJS Library 1.1.1
49201  * Copyright(c) 2008-2011  Alan Knowles
49202  *
49203  * License - LGPL
49204  */
49205  
49206
49207 /**
49208  * @class Roo.form.ComboCheck
49209  * @extends Roo.form.ComboBox
49210  * A combobox for multiple select items.
49211  *
49212  * FIXME - could do with a reset button..
49213  * 
49214  * @constructor
49215  * Create a new ComboCheck
49216  * @param {Object} config Configuration options
49217  */
49218 Roo.form.ComboCheck = function(config){
49219     Roo.form.ComboCheck.superclass.constructor.call(this, config);
49220     // should verify some data...
49221     // like
49222     // hiddenName = required..
49223     // displayField = required
49224     // valudField == required
49225     var req= [ 'hiddenName', 'displayField', 'valueField' ];
49226     var _t = this;
49227     Roo.each(req, function(e) {
49228         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
49229             throw "Roo.form.ComboCheck : missing value for: " + e;
49230         }
49231     });
49232     
49233     
49234 };
49235
49236 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
49237      
49238      
49239     editable : false,
49240      
49241     selectedClass: 'x-menu-item-checked', 
49242     
49243     // private
49244     onRender : function(ct, position){
49245         var _t = this;
49246         
49247         
49248         
49249         if(!this.tpl){
49250             var cls = 'x-combo-list';
49251
49252             
49253             this.tpl =  new Roo.Template({
49254                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
49255                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
49256                    '<span>{' + this.displayField + '}</span>' +
49257                     '</div>' 
49258                 
49259             });
49260         }
49261  
49262         
49263         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
49264         this.view.singleSelect = false;
49265         this.view.multiSelect = true;
49266         this.view.toggleSelect = true;
49267         this.pageTb.add(new Roo.Toolbar.Fill(), {
49268             
49269             text: 'Done',
49270             handler: function()
49271             {
49272                 _t.collapse();
49273             }
49274         });
49275     },
49276     
49277     onViewOver : function(e, t){
49278         // do nothing...
49279         return;
49280         
49281     },
49282     
49283     onViewClick : function(doFocus,index){
49284         return;
49285         
49286     },
49287     select: function () {
49288         //Roo.log("SELECT CALLED");
49289     },
49290      
49291     selectByValue : function(xv, scrollIntoView){
49292         var ar = this.getValueArray();
49293         var sels = [];
49294         
49295         Roo.each(ar, function(v) {
49296             if(v === undefined || v === null){
49297                 return;
49298             }
49299             var r = this.findRecord(this.valueField, v);
49300             if(r){
49301                 sels.push(this.store.indexOf(r))
49302                 
49303             }
49304         },this);
49305         this.view.select(sels);
49306         return false;
49307     },
49308     
49309     
49310     
49311     onSelect : function(record, index){
49312        // Roo.log("onselect Called");
49313        // this is only called by the clear button now..
49314         this.view.clearSelections();
49315         this.setValue('[]');
49316         if (this.value != this.valueBefore) {
49317             this.fireEvent('change', this, this.value, this.valueBefore);
49318             this.valueBefore = this.value;
49319         }
49320     },
49321     getValueArray : function()
49322     {
49323         var ar = [] ;
49324         
49325         try {
49326             //Roo.log(this.value);
49327             if (typeof(this.value) == 'undefined') {
49328                 return [];
49329             }
49330             var ar = Roo.decode(this.value);
49331             return  ar instanceof Array ? ar : []; //?? valid?
49332             
49333         } catch(e) {
49334             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
49335             return [];
49336         }
49337          
49338     },
49339     expand : function ()
49340     {
49341         
49342         Roo.form.ComboCheck.superclass.expand.call(this);
49343         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
49344         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
49345         
49346
49347     },
49348     
49349     collapse : function(){
49350         Roo.form.ComboCheck.superclass.collapse.call(this);
49351         var sl = this.view.getSelectedIndexes();
49352         var st = this.store;
49353         var nv = [];
49354         var tv = [];
49355         var r;
49356         Roo.each(sl, function(i) {
49357             r = st.getAt(i);
49358             nv.push(r.get(this.valueField));
49359         },this);
49360         this.setValue(Roo.encode(nv));
49361         if (this.value != this.valueBefore) {
49362
49363             this.fireEvent('change', this, this.value, this.valueBefore);
49364             this.valueBefore = this.value;
49365         }
49366         
49367     },
49368     
49369     setValue : function(v){
49370         // Roo.log(v);
49371         this.value = v;
49372         
49373         var vals = this.getValueArray();
49374         var tv = [];
49375         Roo.each(vals, function(k) {
49376             var r = this.findRecord(this.valueField, k);
49377             if(r){
49378                 tv.push(r.data[this.displayField]);
49379             }else if(this.valueNotFoundText !== undefined){
49380                 tv.push( this.valueNotFoundText );
49381             }
49382         },this);
49383        // Roo.log(tv);
49384         
49385         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
49386         this.hiddenField.value = v;
49387         this.value = v;
49388     }
49389     
49390 });/*
49391  * Based on:
49392  * Ext JS Library 1.1.1
49393  * Copyright(c) 2006-2007, Ext JS, LLC.
49394  *
49395  * Originally Released Under LGPL - original licence link has changed is not relivant.
49396  *
49397  * Fork - LGPL
49398  * <script type="text/javascript">
49399  */
49400  
49401 /**
49402  * @class Roo.form.Signature
49403  * @extends Roo.form.Field
49404  * Signature field.  
49405  * @constructor
49406  * 
49407  * @param {Object} config Configuration options
49408  */
49409
49410 Roo.form.Signature = function(config){
49411     Roo.form.Signature.superclass.constructor.call(this, config);
49412     
49413     this.addEvents({// not in used??
49414          /**
49415          * @event confirm
49416          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
49417              * @param {Roo.form.Signature} combo This combo box
49418              */
49419         'confirm' : true,
49420         /**
49421          * @event reset
49422          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
49423              * @param {Roo.form.ComboBox} combo This combo box
49424              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
49425              */
49426         'reset' : true
49427     });
49428 };
49429
49430 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
49431     /**
49432      * @cfg {Object} labels Label to use when rendering a form.
49433      * defaults to 
49434      * labels : { 
49435      *      clear : "Clear",
49436      *      confirm : "Confirm"
49437      *  }
49438      */
49439     labels : { 
49440         clear : "Clear",
49441         confirm : "Confirm"
49442     },
49443     /**
49444      * @cfg {Number} width The signature panel width (defaults to 300)
49445      */
49446     width: 300,
49447     /**
49448      * @cfg {Number} height The signature panel height (defaults to 100)
49449      */
49450     height : 100,
49451     /**
49452      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
49453      */
49454     allowBlank : false,
49455     
49456     //private
49457     // {Object} signPanel The signature SVG panel element (defaults to {})
49458     signPanel : {},
49459     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
49460     isMouseDown : false,
49461     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
49462     isConfirmed : false,
49463     // {String} signatureTmp SVG mapping string (defaults to empty string)
49464     signatureTmp : '',
49465     
49466     
49467     defaultAutoCreate : { // modified by initCompnoent..
49468         tag: "input",
49469         type:"hidden"
49470     },
49471
49472     // private
49473     onRender : function(ct, position){
49474         
49475         Roo.form.Signature.superclass.onRender.call(this, ct, position);
49476         
49477         this.wrap = this.el.wrap({
49478             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
49479         });
49480         
49481         this.createToolbar(this);
49482         this.signPanel = this.wrap.createChild({
49483                 tag: 'div',
49484                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
49485             }, this.el
49486         );
49487             
49488         this.svgID = Roo.id();
49489         this.svgEl = this.signPanel.createChild({
49490               xmlns : 'http://www.w3.org/2000/svg',
49491               tag : 'svg',
49492               id : this.svgID + "-svg",
49493               width: this.width,
49494               height: this.height,
49495               viewBox: '0 0 '+this.width+' '+this.height,
49496               cn : [
49497                 {
49498                     tag: "rect",
49499                     id: this.svgID + "-svg-r",
49500                     width: this.width,
49501                     height: this.height,
49502                     fill: "#ffa"
49503                 },
49504                 {
49505                     tag: "line",
49506                     id: this.svgID + "-svg-l",
49507                     x1: "0", // start
49508                     y1: (this.height*0.8), // start set the line in 80% of height
49509                     x2: this.width, // end
49510                     y2: (this.height*0.8), // end set the line in 80% of height
49511                     'stroke': "#666",
49512                     'stroke-width': "1",
49513                     'stroke-dasharray': "3",
49514                     'shape-rendering': "crispEdges",
49515                     'pointer-events': "none"
49516                 },
49517                 {
49518                     tag: "path",
49519                     id: this.svgID + "-svg-p",
49520                     'stroke': "navy",
49521                     'stroke-width': "3",
49522                     'fill': "none",
49523                     'pointer-events': 'none'
49524                 }
49525               ]
49526         });
49527         this.createSVG();
49528         this.svgBox = this.svgEl.dom.getScreenCTM();
49529     },
49530     createSVG : function(){ 
49531         var svg = this.signPanel;
49532         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
49533         var t = this;
49534
49535         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
49536         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
49537         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
49538         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
49539         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
49540         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
49541         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
49542         
49543     },
49544     isTouchEvent : function(e){
49545         return e.type.match(/^touch/);
49546     },
49547     getCoords : function (e) {
49548         var pt    = this.svgEl.dom.createSVGPoint();
49549         pt.x = e.clientX; 
49550         pt.y = e.clientY;
49551         if (this.isTouchEvent(e)) {
49552             pt.x =  e.targetTouches[0].clientX;
49553             pt.y = e.targetTouches[0].clientY;
49554         }
49555         var a = this.svgEl.dom.getScreenCTM();
49556         var b = a.inverse();
49557         var mx = pt.matrixTransform(b);
49558         return mx.x + ',' + mx.y;
49559     },
49560     //mouse event headler 
49561     down : function (e) {
49562         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
49563         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
49564         
49565         this.isMouseDown = true;
49566         
49567         e.preventDefault();
49568     },
49569     move : function (e) {
49570         if (this.isMouseDown) {
49571             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
49572             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
49573         }
49574         
49575         e.preventDefault();
49576     },
49577     up : function (e) {
49578         this.isMouseDown = false;
49579         var sp = this.signatureTmp.split(' ');
49580         
49581         if(sp.length > 1){
49582             if(!sp[sp.length-2].match(/^L/)){
49583                 sp.pop();
49584                 sp.pop();
49585                 sp.push("");
49586                 this.signatureTmp = sp.join(" ");
49587             }
49588         }
49589         if(this.getValue() != this.signatureTmp){
49590             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49591             this.isConfirmed = false;
49592         }
49593         e.preventDefault();
49594     },
49595     
49596     /**
49597      * Protected method that will not generally be called directly. It
49598      * is called when the editor creates its toolbar. Override this method if you need to
49599      * add custom toolbar buttons.
49600      * @param {HtmlEditor} editor
49601      */
49602     createToolbar : function(editor){
49603          function btn(id, toggle, handler){
49604             var xid = fid + '-'+ id ;
49605             return {
49606                 id : xid,
49607                 cmd : id,
49608                 cls : 'x-btn-icon x-edit-'+id,
49609                 enableToggle:toggle !== false,
49610                 scope: editor, // was editor...
49611                 handler:handler||editor.relayBtnCmd,
49612                 clickEvent:'mousedown',
49613                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49614                 tabIndex:-1
49615             };
49616         }
49617         
49618         
49619         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
49620         this.tb = tb;
49621         this.tb.add(
49622            {
49623                 cls : ' x-signature-btn x-signature-'+id,
49624                 scope: editor, // was editor...
49625                 handler: this.reset,
49626                 clickEvent:'mousedown',
49627                 text: this.labels.clear
49628             },
49629             {
49630                  xtype : 'Fill',
49631                  xns: Roo.Toolbar
49632             }, 
49633             {
49634                 cls : '  x-signature-btn x-signature-'+id,
49635                 scope: editor, // was editor...
49636                 handler: this.confirmHandler,
49637                 clickEvent:'mousedown',
49638                 text: this.labels.confirm
49639             }
49640         );
49641     
49642     },
49643     //public
49644     /**
49645      * when user is clicked confirm then show this image.....
49646      * 
49647      * @return {String} Image Data URI
49648      */
49649     getImageDataURI : function(){
49650         var svg = this.svgEl.dom.parentNode.innerHTML;
49651         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
49652         return src; 
49653     },
49654     /**
49655      * 
49656      * @return {Boolean} this.isConfirmed
49657      */
49658     getConfirmed : function(){
49659         return this.isConfirmed;
49660     },
49661     /**
49662      * 
49663      * @return {Number} this.width
49664      */
49665     getWidth : function(){
49666         return this.width;
49667     },
49668     /**
49669      * 
49670      * @return {Number} this.height
49671      */
49672     getHeight : function(){
49673         return this.height;
49674     },
49675     // private
49676     getSignature : function(){
49677         return this.signatureTmp;
49678     },
49679     // private
49680     reset : function(){
49681         this.signatureTmp = '';
49682         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49683         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
49684         this.isConfirmed = false;
49685         Roo.form.Signature.superclass.reset.call(this);
49686     },
49687     setSignature : function(s){
49688         this.signatureTmp = s;
49689         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49690         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
49691         this.setValue(s);
49692         this.isConfirmed = false;
49693         Roo.form.Signature.superclass.reset.call(this);
49694     }, 
49695     test : function(){
49696 //        Roo.log(this.signPanel.dom.contentWindow.up())
49697     },
49698     //private
49699     setConfirmed : function(){
49700         
49701         
49702         
49703 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
49704     },
49705     // private
49706     confirmHandler : function(){
49707         if(!this.getSignature()){
49708             return;
49709         }
49710         
49711         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
49712         this.setValue(this.getSignature());
49713         this.isConfirmed = true;
49714         
49715         this.fireEvent('confirm', this);
49716     },
49717     // private
49718     // Subclasses should provide the validation implementation by overriding this
49719     validateValue : function(value){
49720         if(this.allowBlank){
49721             return true;
49722         }
49723         
49724         if(this.isConfirmed){
49725             return true;
49726         }
49727         return false;
49728     }
49729 });/*
49730  * Based on:
49731  * Ext JS Library 1.1.1
49732  * Copyright(c) 2006-2007, Ext JS, LLC.
49733  *
49734  * Originally Released Under LGPL - original licence link has changed is not relivant.
49735  *
49736  * Fork - LGPL
49737  * <script type="text/javascript">
49738  */
49739  
49740
49741 /**
49742  * @class Roo.form.ComboBox
49743  * @extends Roo.form.TriggerField
49744  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
49745  * @constructor
49746  * Create a new ComboBox.
49747  * @param {Object} config Configuration options
49748  */
49749 Roo.form.Select = function(config){
49750     Roo.form.Select.superclass.constructor.call(this, config);
49751      
49752 };
49753
49754 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
49755     /**
49756      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
49757      */
49758     /**
49759      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
49760      * rendering into an Roo.Editor, defaults to false)
49761      */
49762     /**
49763      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
49764      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
49765      */
49766     /**
49767      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
49768      */
49769     /**
49770      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
49771      * the dropdown list (defaults to undefined, with no header element)
49772      */
49773
49774      /**
49775      * @cfg {String/Roo.Template} tpl The template to use to render the output
49776      */
49777      
49778     // private
49779     defaultAutoCreate : {tag: "select"  },
49780     /**
49781      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
49782      */
49783     listWidth: undefined,
49784     /**
49785      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
49786      * mode = 'remote' or 'text' if mode = 'local')
49787      */
49788     displayField: undefined,
49789     /**
49790      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
49791      * mode = 'remote' or 'value' if mode = 'local'). 
49792      * Note: use of a valueField requires the user make a selection
49793      * in order for a value to be mapped.
49794      */
49795     valueField: undefined,
49796     
49797     
49798     /**
49799      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
49800      * field's data value (defaults to the underlying DOM element's name)
49801      */
49802     hiddenName: undefined,
49803     /**
49804      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
49805      */
49806     listClass: '',
49807     /**
49808      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
49809      */
49810     selectedClass: 'x-combo-selected',
49811     /**
49812      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
49813      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
49814      * which displays a downward arrow icon).
49815      */
49816     triggerClass : 'x-form-arrow-trigger',
49817     /**
49818      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
49819      */
49820     shadow:'sides',
49821     /**
49822      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
49823      * anchor positions (defaults to 'tl-bl')
49824      */
49825     listAlign: 'tl-bl?',
49826     /**
49827      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
49828      */
49829     maxHeight: 300,
49830     /**
49831      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
49832      * query specified by the allQuery config option (defaults to 'query')
49833      */
49834     triggerAction: 'query',
49835     /**
49836      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
49837      * (defaults to 4, does not apply if editable = false)
49838      */
49839     minChars : 4,
49840     /**
49841      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
49842      * delay (typeAheadDelay) if it matches a known value (defaults to false)
49843      */
49844     typeAhead: false,
49845     /**
49846      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
49847      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
49848      */
49849     queryDelay: 500,
49850     /**
49851      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
49852      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
49853      */
49854     pageSize: 0,
49855     /**
49856      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
49857      * when editable = true (defaults to false)
49858      */
49859     selectOnFocus:false,
49860     /**
49861      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
49862      */
49863     queryParam: 'query',
49864     /**
49865      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
49866      * when mode = 'remote' (defaults to 'Loading...')
49867      */
49868     loadingText: 'Loading...',
49869     /**
49870      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
49871      */
49872     resizable: false,
49873     /**
49874      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
49875      */
49876     handleHeight : 8,
49877     /**
49878      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
49879      * traditional select (defaults to true)
49880      */
49881     editable: true,
49882     /**
49883      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
49884      */
49885     allQuery: '',
49886     /**
49887      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
49888      */
49889     mode: 'remote',
49890     /**
49891      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
49892      * listWidth has a higher value)
49893      */
49894     minListWidth : 70,
49895     /**
49896      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
49897      * allow the user to set arbitrary text into the field (defaults to false)
49898      */
49899     forceSelection:false,
49900     /**
49901      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
49902      * if typeAhead = true (defaults to 250)
49903      */
49904     typeAheadDelay : 250,
49905     /**
49906      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
49907      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
49908      */
49909     valueNotFoundText : undefined,
49910     
49911     /**
49912      * @cfg {String} defaultValue The value displayed after loading the store.
49913      */
49914     defaultValue: '',
49915     
49916     /**
49917      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
49918      */
49919     blockFocus : false,
49920     
49921     /**
49922      * @cfg {Boolean} disableClear Disable showing of clear button.
49923      */
49924     disableClear : false,
49925     /**
49926      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
49927      */
49928     alwaysQuery : false,
49929     
49930     //private
49931     addicon : false,
49932     editicon: false,
49933     
49934     // element that contains real text value.. (when hidden is used..)
49935      
49936     // private
49937     onRender : function(ct, position){
49938         Roo.form.Field.prototype.onRender.call(this, ct, position);
49939         
49940         if(this.store){
49941             this.store.on('beforeload', this.onBeforeLoad, this);
49942             this.store.on('load', this.onLoad, this);
49943             this.store.on('loadexception', this.onLoadException, this);
49944             this.store.load({});
49945         }
49946         
49947         
49948         
49949     },
49950
49951     // private
49952     initEvents : function(){
49953         //Roo.form.ComboBox.superclass.initEvents.call(this);
49954  
49955     },
49956
49957     onDestroy : function(){
49958        
49959         if(this.store){
49960             this.store.un('beforeload', this.onBeforeLoad, this);
49961             this.store.un('load', this.onLoad, this);
49962             this.store.un('loadexception', this.onLoadException, this);
49963         }
49964         //Roo.form.ComboBox.superclass.onDestroy.call(this);
49965     },
49966
49967     // private
49968     fireKey : function(e){
49969         if(e.isNavKeyPress() && !this.list.isVisible()){
49970             this.fireEvent("specialkey", this, e);
49971         }
49972     },
49973
49974     // private
49975     onResize: function(w, h){
49976         
49977         return; 
49978     
49979         
49980     },
49981
49982     /**
49983      * Allow or prevent the user from directly editing the field text.  If false is passed,
49984      * the user will only be able to select from the items defined in the dropdown list.  This method
49985      * is the runtime equivalent of setting the 'editable' config option at config time.
49986      * @param {Boolean} value True to allow the user to directly edit the field text
49987      */
49988     setEditable : function(value){
49989          
49990     },
49991
49992     // private
49993     onBeforeLoad : function(){
49994         
49995         Roo.log("Select before load");
49996         return;
49997     
49998         this.innerList.update(this.loadingText ?
49999                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
50000         //this.restrictHeight();
50001         this.selectedIndex = -1;
50002     },
50003
50004     // private
50005     onLoad : function(){
50006
50007     
50008         var dom = this.el.dom;
50009         dom.innerHTML = '';
50010          var od = dom.ownerDocument;
50011          
50012         if (this.emptyText) {
50013             var op = od.createElement('option');
50014             op.setAttribute('value', '');
50015             op.innerHTML = String.format('{0}', this.emptyText);
50016             dom.appendChild(op);
50017         }
50018         if(this.store.getCount() > 0){
50019            
50020             var vf = this.valueField;
50021             var df = this.displayField;
50022             this.store.data.each(function(r) {
50023                 // which colmsn to use... testing - cdoe / title..
50024                 var op = od.createElement('option');
50025                 op.setAttribute('value', r.data[vf]);
50026                 op.innerHTML = String.format('{0}', r.data[df]);
50027                 dom.appendChild(op);
50028             });
50029             if (typeof(this.defaultValue != 'undefined')) {
50030                 this.setValue(this.defaultValue);
50031             }
50032             
50033              
50034         }else{
50035             //this.onEmptyResults();
50036         }
50037         //this.el.focus();
50038     },
50039     // private
50040     onLoadException : function()
50041     {
50042         dom.innerHTML = '';
50043             
50044         Roo.log("Select on load exception");
50045         return;
50046     
50047         this.collapse();
50048         Roo.log(this.store.reader.jsonData);
50049         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
50050             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
50051         }
50052         
50053         
50054     },
50055     // private
50056     onTypeAhead : function(){
50057          
50058     },
50059
50060     // private
50061     onSelect : function(record, index){
50062         Roo.log('on select?');
50063         return;
50064         if(this.fireEvent('beforeselect', this, record, index) !== false){
50065             this.setFromData(index > -1 ? record.data : false);
50066             this.collapse();
50067             this.fireEvent('select', this, record, index);
50068         }
50069     },
50070
50071     /**
50072      * Returns the currently selected field value or empty string if no value is set.
50073      * @return {String} value The selected value
50074      */
50075     getValue : function(){
50076         var dom = this.el.dom;
50077         this.value = dom.options[dom.selectedIndex].value;
50078         return this.value;
50079         
50080     },
50081
50082     /**
50083      * Clears any text/value currently set in the field
50084      */
50085     clearValue : function(){
50086         this.value = '';
50087         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
50088         
50089     },
50090
50091     /**
50092      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
50093      * will be displayed in the field.  If the value does not match the data value of an existing item,
50094      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
50095      * Otherwise the field will be blank (although the value will still be set).
50096      * @param {String} value The value to match
50097      */
50098     setValue : function(v){
50099         var d = this.el.dom;
50100         for (var i =0; i < d.options.length;i++) {
50101             if (v == d.options[i].value) {
50102                 d.selectedIndex = i;
50103                 this.value = v;
50104                 return;
50105             }
50106         }
50107         this.clearValue();
50108     },
50109     /**
50110      * @property {Object} the last set data for the element
50111      */
50112     
50113     lastData : false,
50114     /**
50115      * Sets the value of the field based on a object which is related to the record format for the store.
50116      * @param {Object} value the value to set as. or false on reset?
50117      */
50118     setFromData : function(o){
50119         Roo.log('setfrom data?');
50120          
50121         
50122         
50123     },
50124     // private
50125     reset : function(){
50126         this.clearValue();
50127     },
50128     // private
50129     findRecord : function(prop, value){
50130         
50131         return false;
50132     
50133         var record;
50134         if(this.store.getCount() > 0){
50135             this.store.each(function(r){
50136                 if(r.data[prop] == value){
50137                     record = r;
50138                     return false;
50139                 }
50140                 return true;
50141             });
50142         }
50143         return record;
50144     },
50145     
50146     getName: function()
50147     {
50148         // returns hidden if it's set..
50149         if (!this.rendered) {return ''};
50150         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
50151         
50152     },
50153      
50154
50155     
50156
50157     // private
50158     onEmptyResults : function(){
50159         Roo.log('empty results');
50160         //this.collapse();
50161     },
50162
50163     /**
50164      * Returns true if the dropdown list is expanded, else false.
50165      */
50166     isExpanded : function(){
50167         return false;
50168     },
50169
50170     /**
50171      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
50172      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50173      * @param {String} value The data value of the item to select
50174      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50175      * selected item if it is not currently in view (defaults to true)
50176      * @return {Boolean} True if the value matched an item in the list, else false
50177      */
50178     selectByValue : function(v, scrollIntoView){
50179         Roo.log('select By Value');
50180         return false;
50181     
50182         if(v !== undefined && v !== null){
50183             var r = this.findRecord(this.valueField || this.displayField, v);
50184             if(r){
50185                 this.select(this.store.indexOf(r), scrollIntoView);
50186                 return true;
50187             }
50188         }
50189         return false;
50190     },
50191
50192     /**
50193      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
50194      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50195      * @param {Number} index The zero-based index of the list item to select
50196      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50197      * selected item if it is not currently in view (defaults to true)
50198      */
50199     select : function(index, scrollIntoView){
50200         Roo.log('select ');
50201         return  ;
50202         
50203         this.selectedIndex = index;
50204         this.view.select(index);
50205         if(scrollIntoView !== false){
50206             var el = this.view.getNode(index);
50207             if(el){
50208                 this.innerList.scrollChildIntoView(el, false);
50209             }
50210         }
50211     },
50212
50213       
50214
50215     // private
50216     validateBlur : function(){
50217         
50218         return;
50219         
50220     },
50221
50222     // private
50223     initQuery : function(){
50224         this.doQuery(this.getRawValue());
50225     },
50226
50227     // private
50228     doForce : function(){
50229         if(this.el.dom.value.length > 0){
50230             this.el.dom.value =
50231                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
50232              
50233         }
50234     },
50235
50236     /**
50237      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
50238      * query allowing the query action to be canceled if needed.
50239      * @param {String} query The SQL query to execute
50240      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
50241      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
50242      * saved in the current store (defaults to false)
50243      */
50244     doQuery : function(q, forceAll){
50245         
50246         Roo.log('doQuery?');
50247         if(q === undefined || q === null){
50248             q = '';
50249         }
50250         var qe = {
50251             query: q,
50252             forceAll: forceAll,
50253             combo: this,
50254             cancel:false
50255         };
50256         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
50257             return false;
50258         }
50259         q = qe.query;
50260         forceAll = qe.forceAll;
50261         if(forceAll === true || (q.length >= this.minChars)){
50262             if(this.lastQuery != q || this.alwaysQuery){
50263                 this.lastQuery = q;
50264                 if(this.mode == 'local'){
50265                     this.selectedIndex = -1;
50266                     if(forceAll){
50267                         this.store.clearFilter();
50268                     }else{
50269                         this.store.filter(this.displayField, q);
50270                     }
50271                     this.onLoad();
50272                 }else{
50273                     this.store.baseParams[this.queryParam] = q;
50274                     this.store.load({
50275                         params: this.getParams(q)
50276                     });
50277                     this.expand();
50278                 }
50279             }else{
50280                 this.selectedIndex = -1;
50281                 this.onLoad();   
50282             }
50283         }
50284     },
50285
50286     // private
50287     getParams : function(q){
50288         var p = {};
50289         //p[this.queryParam] = q;
50290         if(this.pageSize){
50291             p.start = 0;
50292             p.limit = this.pageSize;
50293         }
50294         return p;
50295     },
50296
50297     /**
50298      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
50299      */
50300     collapse : function(){
50301         
50302     },
50303
50304     // private
50305     collapseIf : function(e){
50306         
50307     },
50308
50309     /**
50310      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
50311      */
50312     expand : function(){
50313         
50314     } ,
50315
50316     // private
50317      
50318
50319     /** 
50320     * @cfg {Boolean} grow 
50321     * @hide 
50322     */
50323     /** 
50324     * @cfg {Number} growMin 
50325     * @hide 
50326     */
50327     /** 
50328     * @cfg {Number} growMax 
50329     * @hide 
50330     */
50331     /**
50332      * @hide
50333      * @method autoSize
50334      */
50335     
50336     setWidth : function()
50337     {
50338         
50339     },
50340     getResizeEl : function(){
50341         return this.el;
50342     }
50343 });//<script type="text/javasscript">
50344  
50345
50346 /**
50347  * @class Roo.DDView
50348  * A DnD enabled version of Roo.View.
50349  * @param {Element/String} container The Element in which to create the View.
50350  * @param {String} tpl The template string used to create the markup for each element of the View
50351  * @param {Object} config The configuration properties. These include all the config options of
50352  * {@link Roo.View} plus some specific to this class.<br>
50353  * <p>
50354  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
50355  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
50356  * <p>
50357  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
50358 .x-view-drag-insert-above {
50359         border-top:1px dotted #3366cc;
50360 }
50361 .x-view-drag-insert-below {
50362         border-bottom:1px dotted #3366cc;
50363 }
50364 </code></pre>
50365  * 
50366  */
50367  
50368 Roo.DDView = function(container, tpl, config) {
50369     Roo.DDView.superclass.constructor.apply(this, arguments);
50370     this.getEl().setStyle("outline", "0px none");
50371     this.getEl().unselectable();
50372     if (this.dragGroup) {
50373                 this.setDraggable(this.dragGroup.split(","));
50374     }
50375     if (this.dropGroup) {
50376                 this.setDroppable(this.dropGroup.split(","));
50377     }
50378     if (this.deletable) {
50379         this.setDeletable();
50380     }
50381     this.isDirtyFlag = false;
50382         this.addEvents({
50383                 "drop" : true
50384         });
50385 };
50386
50387 Roo.extend(Roo.DDView, Roo.View, {
50388 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
50389 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
50390 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
50391 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
50392
50393         isFormField: true,
50394
50395         reset: Roo.emptyFn,
50396         
50397         clearInvalid: Roo.form.Field.prototype.clearInvalid,
50398
50399         validate: function() {
50400                 return true;
50401         },
50402         
50403         destroy: function() {
50404                 this.purgeListeners();
50405                 this.getEl.removeAllListeners();
50406                 this.getEl().remove();
50407                 if (this.dragZone) {
50408                         if (this.dragZone.destroy) {
50409                                 this.dragZone.destroy();
50410                         }
50411                 }
50412                 if (this.dropZone) {
50413                         if (this.dropZone.destroy) {
50414                                 this.dropZone.destroy();
50415                         }
50416                 }
50417         },
50418
50419 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
50420         getName: function() {
50421                 return this.name;
50422         },
50423
50424 /**     Loads the View from a JSON string representing the Records to put into the Store. */
50425         setValue: function(v) {
50426                 if (!this.store) {
50427                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
50428                 }
50429                 var data = {};
50430                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
50431                 this.store.proxy = new Roo.data.MemoryProxy(data);
50432                 this.store.load();
50433         },
50434
50435 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
50436         getValue: function() {
50437                 var result = '(';
50438                 this.store.each(function(rec) {
50439                         result += rec.id + ',';
50440                 });
50441                 return result.substr(0, result.length - 1) + ')';
50442         },
50443         
50444         getIds: function() {
50445                 var i = 0, result = new Array(this.store.getCount());
50446                 this.store.each(function(rec) {
50447                         result[i++] = rec.id;
50448                 });
50449                 return result;
50450         },
50451         
50452         isDirty: function() {
50453                 return this.isDirtyFlag;
50454         },
50455
50456 /**
50457  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
50458  *      whole Element becomes the target, and this causes the drop gesture to append.
50459  */
50460     getTargetFromEvent : function(e) {
50461                 var target = e.getTarget();
50462                 while ((target !== null) && (target.parentNode != this.el.dom)) {
50463                 target = target.parentNode;
50464                 }
50465                 if (!target) {
50466                         target = this.el.dom.lastChild || this.el.dom;
50467                 }
50468                 return target;
50469     },
50470
50471 /**
50472  *      Create the drag data which consists of an object which has the property "ddel" as
50473  *      the drag proxy element. 
50474  */
50475     getDragData : function(e) {
50476         var target = this.findItemFromChild(e.getTarget());
50477                 if(target) {
50478                         this.handleSelection(e);
50479                         var selNodes = this.getSelectedNodes();
50480             var dragData = {
50481                 source: this,
50482                 copy: this.copy || (this.allowCopy && e.ctrlKey),
50483                 nodes: selNodes,
50484                 records: []
50485                         };
50486                         var selectedIndices = this.getSelectedIndexes();
50487                         for (var i = 0; i < selectedIndices.length; i++) {
50488                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
50489                         }
50490                         if (selNodes.length == 1) {
50491                                 dragData.ddel = target.cloneNode(true); // the div element
50492                         } else {
50493                                 var div = document.createElement('div'); // create the multi element drag "ghost"
50494                                 div.className = 'multi-proxy';
50495                                 for (var i = 0, len = selNodes.length; i < len; i++) {
50496                                         div.appendChild(selNodes[i].cloneNode(true));
50497                                 }
50498                                 dragData.ddel = div;
50499                         }
50500             //console.log(dragData)
50501             //console.log(dragData.ddel.innerHTML)
50502                         return dragData;
50503                 }
50504         //console.log('nodragData')
50505                 return false;
50506     },
50507     
50508 /**     Specify to which ddGroup items in this DDView may be dragged. */
50509     setDraggable: function(ddGroup) {
50510         if (ddGroup instanceof Array) {
50511                 Roo.each(ddGroup, this.setDraggable, this);
50512                 return;
50513         }
50514         if (this.dragZone) {
50515                 this.dragZone.addToGroup(ddGroup);
50516         } else {
50517                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
50518                                 containerScroll: true,
50519                                 ddGroup: ddGroup 
50520
50521                         });
50522 //                      Draggability implies selection. DragZone's mousedown selects the element.
50523                         if (!this.multiSelect) { this.singleSelect = true; }
50524
50525 //                      Wire the DragZone's handlers up to methods in *this*
50526                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
50527                 }
50528     },
50529
50530 /**     Specify from which ddGroup this DDView accepts drops. */
50531     setDroppable: function(ddGroup) {
50532         if (ddGroup instanceof Array) {
50533                 Roo.each(ddGroup, this.setDroppable, this);
50534                 return;
50535         }
50536         if (this.dropZone) {
50537                 this.dropZone.addToGroup(ddGroup);
50538         } else {
50539                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
50540                                 containerScroll: true,
50541                                 ddGroup: ddGroup
50542                         });
50543
50544 //                      Wire the DropZone's handlers up to methods in *this*
50545                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
50546                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
50547                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
50548                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
50549                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
50550                 }
50551     },
50552
50553 /**     Decide whether to drop above or below a View node. */
50554     getDropPoint : function(e, n, dd){
50555         if (n == this.el.dom) { return "above"; }
50556                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
50557                 var c = t + (b - t) / 2;
50558                 var y = Roo.lib.Event.getPageY(e);
50559                 if(y <= c) {
50560                         return "above";
50561                 }else{
50562                         return "below";
50563                 }
50564     },
50565
50566     onNodeEnter : function(n, dd, e, data){
50567                 return false;
50568     },
50569     
50570     onNodeOver : function(n, dd, e, data){
50571                 var pt = this.getDropPoint(e, n, dd);
50572                 // set the insert point style on the target node
50573                 var dragElClass = this.dropNotAllowed;
50574                 if (pt) {
50575                         var targetElClass;
50576                         if (pt == "above"){
50577                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
50578                                 targetElClass = "x-view-drag-insert-above";
50579                         } else {
50580                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
50581                                 targetElClass = "x-view-drag-insert-below";
50582                         }
50583                         if (this.lastInsertClass != targetElClass){
50584                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
50585                                 this.lastInsertClass = targetElClass;
50586                         }
50587                 }
50588                 return dragElClass;
50589         },
50590
50591     onNodeOut : function(n, dd, e, data){
50592                 this.removeDropIndicators(n);
50593     },
50594
50595     onNodeDrop : function(n, dd, e, data){
50596         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
50597                 return false;
50598         }
50599         var pt = this.getDropPoint(e, n, dd);
50600                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
50601                 if (pt == "below") { insertAt++; }
50602                 for (var i = 0; i < data.records.length; i++) {
50603                         var r = data.records[i];
50604                         var dup = this.store.getById(r.id);
50605                         if (dup && (dd != this.dragZone)) {
50606                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
50607                         } else {
50608                                 if (data.copy) {
50609                                         this.store.insert(insertAt++, r.copy());
50610                                 } else {
50611                                         data.source.isDirtyFlag = true;
50612                                         r.store.remove(r);
50613                                         this.store.insert(insertAt++, r);
50614                                 }
50615                                 this.isDirtyFlag = true;
50616                         }
50617                 }
50618                 this.dragZone.cachedTarget = null;
50619                 return true;
50620     },
50621
50622     removeDropIndicators : function(n){
50623                 if(n){
50624                         Roo.fly(n).removeClass([
50625                                 "x-view-drag-insert-above",
50626                                 "x-view-drag-insert-below"]);
50627                         this.lastInsertClass = "_noclass";
50628                 }
50629     },
50630
50631 /**
50632  *      Utility method. Add a delete option to the DDView's context menu.
50633  *      @param {String} imageUrl The URL of the "delete" icon image.
50634  */
50635         setDeletable: function(imageUrl) {
50636                 if (!this.singleSelect && !this.multiSelect) {
50637                         this.singleSelect = true;
50638                 }
50639                 var c = this.getContextMenu();
50640                 this.contextMenu.on("itemclick", function(item) {
50641                         switch (item.id) {
50642                                 case "delete":
50643                                         this.remove(this.getSelectedIndexes());
50644                                         break;
50645                         }
50646                 }, this);
50647                 this.contextMenu.add({
50648                         icon: imageUrl,
50649                         id: "delete",
50650                         text: 'Delete'
50651                 });
50652         },
50653         
50654 /**     Return the context menu for this DDView. */
50655         getContextMenu: function() {
50656                 if (!this.contextMenu) {
50657 //                      Create the View's context menu
50658                         this.contextMenu = new Roo.menu.Menu({
50659                                 id: this.id + "-contextmenu"
50660                         });
50661                         this.el.on("contextmenu", this.showContextMenu, this);
50662                 }
50663                 return this.contextMenu;
50664         },
50665         
50666         disableContextMenu: function() {
50667                 if (this.contextMenu) {
50668                         this.el.un("contextmenu", this.showContextMenu, this);
50669                 }
50670         },
50671
50672         showContextMenu: function(e, item) {
50673         item = this.findItemFromChild(e.getTarget());
50674                 if (item) {
50675                         e.stopEvent();
50676                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
50677                         this.contextMenu.showAt(e.getXY());
50678             }
50679     },
50680
50681 /**
50682  *      Remove {@link Roo.data.Record}s at the specified indices.
50683  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
50684  */
50685     remove: function(selectedIndices) {
50686                 selectedIndices = [].concat(selectedIndices);
50687                 for (var i = 0; i < selectedIndices.length; i++) {
50688                         var rec = this.store.getAt(selectedIndices[i]);
50689                         this.store.remove(rec);
50690                 }
50691     },
50692
50693 /**
50694  *      Double click fires the event, but also, if this is draggable, and there is only one other
50695  *      related DropZone, it transfers the selected node.
50696  */
50697     onDblClick : function(e){
50698         var item = this.findItemFromChild(e.getTarget());
50699         if(item){
50700             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
50701                 return false;
50702             }
50703             if (this.dragGroup) {
50704                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
50705                     while (targets.indexOf(this.dropZone) > -1) {
50706                             targets.remove(this.dropZone);
50707                                 }
50708                     if (targets.length == 1) {
50709                                         this.dragZone.cachedTarget = null;
50710                         var el = Roo.get(targets[0].getEl());
50711                         var box = el.getBox(true);
50712                         targets[0].onNodeDrop(el.dom, {
50713                                 target: el.dom,
50714                                 xy: [box.x, box.y + box.height - 1]
50715                         }, null, this.getDragData(e));
50716                     }
50717                 }
50718         }
50719     },
50720     
50721     handleSelection: function(e) {
50722                 this.dragZone.cachedTarget = null;
50723         var item = this.findItemFromChild(e.getTarget());
50724         if (!item) {
50725                 this.clearSelections(true);
50726                 return;
50727         }
50728                 if (item && (this.multiSelect || this.singleSelect)){
50729                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
50730                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
50731                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
50732                                 this.unselect(item);
50733                         } else {
50734                                 this.select(item, this.multiSelect && e.ctrlKey);
50735                                 this.lastSelection = item;
50736                         }
50737                 }
50738     },
50739
50740     onItemClick : function(item, index, e){
50741                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
50742                         return false;
50743                 }
50744                 return true;
50745     },
50746
50747     unselect : function(nodeInfo, suppressEvent){
50748                 var node = this.getNode(nodeInfo);
50749                 if(node && this.isSelected(node)){
50750                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
50751                                 Roo.fly(node).removeClass(this.selectedClass);
50752                                 this.selections.remove(node);
50753                                 if(!suppressEvent){
50754                                         this.fireEvent("selectionchange", this, this.selections);
50755                                 }
50756                         }
50757                 }
50758     }
50759 });
50760 /*
50761  * Based on:
50762  * Ext JS Library 1.1.1
50763  * Copyright(c) 2006-2007, Ext JS, LLC.
50764  *
50765  * Originally Released Under LGPL - original licence link has changed is not relivant.
50766  *
50767  * Fork - LGPL
50768  * <script type="text/javascript">
50769  */
50770  
50771 /**
50772  * @class Roo.LayoutManager
50773  * @extends Roo.util.Observable
50774  * Base class for layout managers.
50775  */
50776 Roo.LayoutManager = function(container, config){
50777     Roo.LayoutManager.superclass.constructor.call(this);
50778     this.el = Roo.get(container);
50779     // ie scrollbar fix
50780     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
50781         document.body.scroll = "no";
50782     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
50783         this.el.position('relative');
50784     }
50785     this.id = this.el.id;
50786     this.el.addClass("x-layout-container");
50787     /** false to disable window resize monitoring @type Boolean */
50788     this.monitorWindowResize = true;
50789     this.regions = {};
50790     this.addEvents({
50791         /**
50792          * @event layout
50793          * Fires when a layout is performed. 
50794          * @param {Roo.LayoutManager} this
50795          */
50796         "layout" : true,
50797         /**
50798          * @event regionresized
50799          * Fires when the user resizes a region. 
50800          * @param {Roo.LayoutRegion} region The resized region
50801          * @param {Number} newSize The new size (width for east/west, height for north/south)
50802          */
50803         "regionresized" : true,
50804         /**
50805          * @event regioncollapsed
50806          * Fires when a region is collapsed. 
50807          * @param {Roo.LayoutRegion} region The collapsed region
50808          */
50809         "regioncollapsed" : true,
50810         /**
50811          * @event regionexpanded
50812          * Fires when a region is expanded.  
50813          * @param {Roo.LayoutRegion} region The expanded region
50814          */
50815         "regionexpanded" : true
50816     });
50817     this.updating = false;
50818     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
50819 };
50820
50821 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
50822     /**
50823      * Returns true if this layout is currently being updated
50824      * @return {Boolean}
50825      */
50826     isUpdating : function(){
50827         return this.updating; 
50828     },
50829     
50830     /**
50831      * Suspend the LayoutManager from doing auto-layouts while
50832      * making multiple add or remove calls
50833      */
50834     beginUpdate : function(){
50835         this.updating = true;    
50836     },
50837     
50838     /**
50839      * Restore auto-layouts and optionally disable the manager from performing a layout
50840      * @param {Boolean} noLayout true to disable a layout update 
50841      */
50842     endUpdate : function(noLayout){
50843         this.updating = false;
50844         if(!noLayout){
50845             this.layout();
50846         }    
50847     },
50848     
50849     layout: function(){
50850         
50851     },
50852     
50853     onRegionResized : function(region, newSize){
50854         this.fireEvent("regionresized", region, newSize);
50855         this.layout();
50856     },
50857     
50858     onRegionCollapsed : function(region){
50859         this.fireEvent("regioncollapsed", region);
50860     },
50861     
50862     onRegionExpanded : function(region){
50863         this.fireEvent("regionexpanded", region);
50864     },
50865         
50866     /**
50867      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
50868      * performs box-model adjustments.
50869      * @return {Object} The size as an object {width: (the width), height: (the height)}
50870      */
50871     getViewSize : function(){
50872         var size;
50873         if(this.el.dom != document.body){
50874             size = this.el.getSize();
50875         }else{
50876             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
50877         }
50878         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
50879         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
50880         return size;
50881     },
50882     
50883     /**
50884      * Returns the Element this layout is bound to.
50885      * @return {Roo.Element}
50886      */
50887     getEl : function(){
50888         return this.el;
50889     },
50890     
50891     /**
50892      * Returns the specified region.
50893      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
50894      * @return {Roo.LayoutRegion}
50895      */
50896     getRegion : function(target){
50897         return this.regions[target.toLowerCase()];
50898     },
50899     
50900     onWindowResize : function(){
50901         if(this.monitorWindowResize){
50902             this.layout();
50903         }
50904     }
50905 });/*
50906  * Based on:
50907  * Ext JS Library 1.1.1
50908  * Copyright(c) 2006-2007, Ext JS, LLC.
50909  *
50910  * Originally Released Under LGPL - original licence link has changed is not relivant.
50911  *
50912  * Fork - LGPL
50913  * <script type="text/javascript">
50914  */
50915 /**
50916  * @class Roo.BorderLayout
50917  * @extends Roo.LayoutManager
50918  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
50919  * please see: <br><br>
50920  * <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>
50921  * <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>
50922  * Example:
50923  <pre><code>
50924  var layout = new Roo.BorderLayout(document.body, {
50925     north: {
50926         initialSize: 25,
50927         titlebar: false
50928     },
50929     west: {
50930         split:true,
50931         initialSize: 200,
50932         minSize: 175,
50933         maxSize: 400,
50934         titlebar: true,
50935         collapsible: true
50936     },
50937     east: {
50938         split:true,
50939         initialSize: 202,
50940         minSize: 175,
50941         maxSize: 400,
50942         titlebar: true,
50943         collapsible: true
50944     },
50945     south: {
50946         split:true,
50947         initialSize: 100,
50948         minSize: 100,
50949         maxSize: 200,
50950         titlebar: true,
50951         collapsible: true
50952     },
50953     center: {
50954         titlebar: true,
50955         autoScroll:true,
50956         resizeTabs: true,
50957         minTabWidth: 50,
50958         preferredTabWidth: 150
50959     }
50960 });
50961
50962 // shorthand
50963 var CP = Roo.ContentPanel;
50964
50965 layout.beginUpdate();
50966 layout.add("north", new CP("north", "North"));
50967 layout.add("south", new CP("south", {title: "South", closable: true}));
50968 layout.add("west", new CP("west", {title: "West"}));
50969 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
50970 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
50971 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
50972 layout.getRegion("center").showPanel("center1");
50973 layout.endUpdate();
50974 </code></pre>
50975
50976 <b>The container the layout is rendered into can be either the body element or any other element.
50977 If it is not the body element, the container needs to either be an absolute positioned element,
50978 or you will need to add "position:relative" to the css of the container.  You will also need to specify
50979 the container size if it is not the body element.</b>
50980
50981 * @constructor
50982 * Create a new BorderLayout
50983 * @param {String/HTMLElement/Element} container The container this layout is bound to
50984 * @param {Object} config Configuration options
50985  */
50986 Roo.BorderLayout = function(container, config){
50987     config = config || {};
50988     Roo.BorderLayout.superclass.constructor.call(this, container, config);
50989     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
50990     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
50991         var target = this.factory.validRegions[i];
50992         if(config[target]){
50993             this.addRegion(target, config[target]);
50994         }
50995     }
50996 };
50997
50998 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
50999     /**
51000      * Creates and adds a new region if it doesn't already exist.
51001      * @param {String} target The target region key (north, south, east, west or center).
51002      * @param {Object} config The regions config object
51003      * @return {BorderLayoutRegion} The new region
51004      */
51005     addRegion : function(target, config){
51006         if(!this.regions[target]){
51007             var r = this.factory.create(target, this, config);
51008             this.bindRegion(target, r);
51009         }
51010         return this.regions[target];
51011     },
51012
51013     // private (kinda)
51014     bindRegion : function(name, r){
51015         this.regions[name] = r;
51016         r.on("visibilitychange", this.layout, this);
51017         r.on("paneladded", this.layout, this);
51018         r.on("panelremoved", this.layout, this);
51019         r.on("invalidated", this.layout, this);
51020         r.on("resized", this.onRegionResized, this);
51021         r.on("collapsed", this.onRegionCollapsed, this);
51022         r.on("expanded", this.onRegionExpanded, this);
51023     },
51024
51025     /**
51026      * Performs a layout update.
51027      */
51028     layout : function(){
51029         if(this.updating) {
51030             return;
51031         }
51032         var size = this.getViewSize();
51033         var w = size.width;
51034         var h = size.height;
51035         var centerW = w;
51036         var centerH = h;
51037         var centerY = 0;
51038         var centerX = 0;
51039         //var x = 0, y = 0;
51040
51041         var rs = this.regions;
51042         var north = rs["north"];
51043         var south = rs["south"]; 
51044         var west = rs["west"];
51045         var east = rs["east"];
51046         var center = rs["center"];
51047         //if(this.hideOnLayout){ // not supported anymore
51048             //c.el.setStyle("display", "none");
51049         //}
51050         if(north && north.isVisible()){
51051             var b = north.getBox();
51052             var m = north.getMargins();
51053             b.width = w - (m.left+m.right);
51054             b.x = m.left;
51055             b.y = m.top;
51056             centerY = b.height + b.y + m.bottom;
51057             centerH -= centerY;
51058             north.updateBox(this.safeBox(b));
51059         }
51060         if(south && south.isVisible()){
51061             var b = south.getBox();
51062             var m = south.getMargins();
51063             b.width = w - (m.left+m.right);
51064             b.x = m.left;
51065             var totalHeight = (b.height + m.top + m.bottom);
51066             b.y = h - totalHeight + m.top;
51067             centerH -= totalHeight;
51068             south.updateBox(this.safeBox(b));
51069         }
51070         if(west && west.isVisible()){
51071             var b = west.getBox();
51072             var m = west.getMargins();
51073             b.height = centerH - (m.top+m.bottom);
51074             b.x = m.left;
51075             b.y = centerY + m.top;
51076             var totalWidth = (b.width + m.left + m.right);
51077             centerX += totalWidth;
51078             centerW -= totalWidth;
51079             west.updateBox(this.safeBox(b));
51080         }
51081         if(east && east.isVisible()){
51082             var b = east.getBox();
51083             var m = east.getMargins();
51084             b.height = centerH - (m.top+m.bottom);
51085             var totalWidth = (b.width + m.left + m.right);
51086             b.x = w - totalWidth + m.left;
51087             b.y = centerY + m.top;
51088             centerW -= totalWidth;
51089             east.updateBox(this.safeBox(b));
51090         }
51091         if(center){
51092             var m = center.getMargins();
51093             var centerBox = {
51094                 x: centerX + m.left,
51095                 y: centerY + m.top,
51096                 width: centerW - (m.left+m.right),
51097                 height: centerH - (m.top+m.bottom)
51098             };
51099             //if(this.hideOnLayout){
51100                 //center.el.setStyle("display", "block");
51101             //}
51102             center.updateBox(this.safeBox(centerBox));
51103         }
51104         this.el.repaint();
51105         this.fireEvent("layout", this);
51106     },
51107
51108     // private
51109     safeBox : function(box){
51110         box.width = Math.max(0, box.width);
51111         box.height = Math.max(0, box.height);
51112         return box;
51113     },
51114
51115     /**
51116      * Adds a ContentPanel (or subclass) to this layout.
51117      * @param {String} target The target region key (north, south, east, west or center).
51118      * @param {Roo.ContentPanel} panel The panel to add
51119      * @return {Roo.ContentPanel} The added panel
51120      */
51121     add : function(target, panel){
51122          
51123         target = target.toLowerCase();
51124         return this.regions[target].add(panel);
51125     },
51126
51127     /**
51128      * Remove a ContentPanel (or subclass) to this layout.
51129      * @param {String} target The target region key (north, south, east, west or center).
51130      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
51131      * @return {Roo.ContentPanel} The removed panel
51132      */
51133     remove : function(target, panel){
51134         target = target.toLowerCase();
51135         return this.regions[target].remove(panel);
51136     },
51137
51138     /**
51139      * Searches all regions for a panel with the specified id
51140      * @param {String} panelId
51141      * @return {Roo.ContentPanel} The panel or null if it wasn't found
51142      */
51143     findPanel : function(panelId){
51144         var rs = this.regions;
51145         for(var target in rs){
51146             if(typeof rs[target] != "function"){
51147                 var p = rs[target].getPanel(panelId);
51148                 if(p){
51149                     return p;
51150                 }
51151             }
51152         }
51153         return null;
51154     },
51155
51156     /**
51157      * Searches all regions for a panel with the specified id and activates (shows) it.
51158      * @param {String/ContentPanel} panelId The panels id or the panel itself
51159      * @return {Roo.ContentPanel} The shown panel or null
51160      */
51161     showPanel : function(panelId) {
51162       var rs = this.regions;
51163       for(var target in rs){
51164          var r = rs[target];
51165          if(typeof r != "function"){
51166             if(r.hasPanel(panelId)){
51167                return r.showPanel(panelId);
51168             }
51169          }
51170       }
51171       return null;
51172    },
51173
51174    /**
51175      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
51176      * @param {Roo.state.Provider} provider (optional) An alternate state provider
51177      */
51178     restoreState : function(provider){
51179         if(!provider){
51180             provider = Roo.state.Manager;
51181         }
51182         var sm = new Roo.LayoutStateManager();
51183         sm.init(this, provider);
51184     },
51185
51186     /**
51187      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
51188      * object should contain properties for each region to add ContentPanels to, and each property's value should be
51189      * a valid ContentPanel config object.  Example:
51190      * <pre><code>
51191 // Create the main layout
51192 var layout = new Roo.BorderLayout('main-ct', {
51193     west: {
51194         split:true,
51195         minSize: 175,
51196         titlebar: true
51197     },
51198     center: {
51199         title:'Components'
51200     }
51201 }, 'main-ct');
51202
51203 // Create and add multiple ContentPanels at once via configs
51204 layout.batchAdd({
51205    west: {
51206        id: 'source-files',
51207        autoCreate:true,
51208        title:'Ext Source Files',
51209        autoScroll:true,
51210        fitToFrame:true
51211    },
51212    center : {
51213        el: cview,
51214        autoScroll:true,
51215        fitToFrame:true,
51216        toolbar: tb,
51217        resizeEl:'cbody'
51218    }
51219 });
51220 </code></pre>
51221      * @param {Object} regions An object containing ContentPanel configs by region name
51222      */
51223     batchAdd : function(regions){
51224         this.beginUpdate();
51225         for(var rname in regions){
51226             var lr = this.regions[rname];
51227             if(lr){
51228                 this.addTypedPanels(lr, regions[rname]);
51229             }
51230         }
51231         this.endUpdate();
51232     },
51233
51234     // private
51235     addTypedPanels : function(lr, ps){
51236         if(typeof ps == 'string'){
51237             lr.add(new Roo.ContentPanel(ps));
51238         }
51239         else if(ps instanceof Array){
51240             for(var i =0, len = ps.length; i < len; i++){
51241                 this.addTypedPanels(lr, ps[i]);
51242             }
51243         }
51244         else if(!ps.events){ // raw config?
51245             var el = ps.el;
51246             delete ps.el; // prevent conflict
51247             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
51248         }
51249         else {  // panel object assumed!
51250             lr.add(ps);
51251         }
51252     },
51253     /**
51254      * Adds a xtype elements to the layout.
51255      * <pre><code>
51256
51257 layout.addxtype({
51258        xtype : 'ContentPanel',
51259        region: 'west',
51260        items: [ .... ]
51261    }
51262 );
51263
51264 layout.addxtype({
51265         xtype : 'NestedLayoutPanel',
51266         region: 'west',
51267         layout: {
51268            center: { },
51269            west: { }   
51270         },
51271         items : [ ... list of content panels or nested layout panels.. ]
51272    }
51273 );
51274 </code></pre>
51275      * @param {Object} cfg Xtype definition of item to add.
51276      */
51277     addxtype : function(cfg)
51278     {
51279         // basically accepts a pannel...
51280         // can accept a layout region..!?!?
51281         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
51282         
51283         if (!cfg.xtype.match(/Panel$/)) {
51284             return false;
51285         }
51286         var ret = false;
51287         
51288         if (typeof(cfg.region) == 'undefined') {
51289             Roo.log("Failed to add Panel, region was not set");
51290             Roo.log(cfg);
51291             return false;
51292         }
51293         var region = cfg.region;
51294         delete cfg.region;
51295         
51296           
51297         var xitems = [];
51298         if (cfg.items) {
51299             xitems = cfg.items;
51300             delete cfg.items;
51301         }
51302         var nb = false;
51303         
51304         switch(cfg.xtype) 
51305         {
51306             case 'ContentPanel':  // ContentPanel (el, cfg)
51307             case 'ScrollPanel':  // ContentPanel (el, cfg)
51308             case 'ViewPanel': 
51309                 if(cfg.autoCreate) {
51310                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51311                 } else {
51312                     var el = this.el.createChild();
51313                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
51314                 }
51315                 
51316                 this.add(region, ret);
51317                 break;
51318             
51319             
51320             case 'TreePanel': // our new panel!
51321                 cfg.el = this.el.createChild();
51322                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51323                 this.add(region, ret);
51324                 break;
51325             
51326             case 'NestedLayoutPanel': 
51327                 // create a new Layout (which is  a Border Layout...
51328                 var el = this.el.createChild();
51329                 var clayout = cfg.layout;
51330                 delete cfg.layout;
51331                 clayout.items   = clayout.items  || [];
51332                 // replace this exitems with the clayout ones..
51333                 xitems = clayout.items;
51334                  
51335                 
51336                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
51337                     cfg.background = false;
51338                 }
51339                 var layout = new Roo.BorderLayout(el, clayout);
51340                 
51341                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
51342                 //console.log('adding nested layout panel '  + cfg.toSource());
51343                 this.add(region, ret);
51344                 nb = {}; /// find first...
51345                 break;
51346                 
51347             case 'GridPanel': 
51348             
51349                 // needs grid and region
51350                 
51351                 //var el = this.getRegion(region).el.createChild();
51352                 var el = this.el.createChild();
51353                 // create the grid first...
51354                 
51355                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
51356                 delete cfg.grid;
51357                 if (region == 'center' && this.active ) {
51358                     cfg.background = false;
51359                 }
51360                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
51361                 
51362                 this.add(region, ret);
51363                 if (cfg.background) {
51364                     ret.on('activate', function(gp) {
51365                         if (!gp.grid.rendered) {
51366                             gp.grid.render();
51367                         }
51368                     });
51369                 } else {
51370                     grid.render();
51371                 }
51372                 break;
51373            
51374            
51375            
51376                 
51377                 
51378                 
51379             default:
51380                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
51381                     
51382                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51383                     this.add(region, ret);
51384                 } else {
51385                 
51386                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
51387                     return null;
51388                 }
51389                 
51390              // GridPanel (grid, cfg)
51391             
51392         }
51393         this.beginUpdate();
51394         // add children..
51395         var region = '';
51396         var abn = {};
51397         Roo.each(xitems, function(i)  {
51398             region = nb && i.region ? i.region : false;
51399             
51400             var add = ret.addxtype(i);
51401            
51402             if (region) {
51403                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
51404                 if (!i.background) {
51405                     abn[region] = nb[region] ;
51406                 }
51407             }
51408             
51409         });
51410         this.endUpdate();
51411
51412         // make the last non-background panel active..
51413         //if (nb) { Roo.log(abn); }
51414         if (nb) {
51415             
51416             for(var r in abn) {
51417                 region = this.getRegion(r);
51418                 if (region) {
51419                     // tried using nb[r], but it does not work..
51420                      
51421                     region.showPanel(abn[r]);
51422                    
51423                 }
51424             }
51425         }
51426         return ret;
51427         
51428     }
51429 });
51430
51431 /**
51432  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
51433  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
51434  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
51435  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
51436  * <pre><code>
51437 // shorthand
51438 var CP = Roo.ContentPanel;
51439
51440 var layout = Roo.BorderLayout.create({
51441     north: {
51442         initialSize: 25,
51443         titlebar: false,
51444         panels: [new CP("north", "North")]
51445     },
51446     west: {
51447         split:true,
51448         initialSize: 200,
51449         minSize: 175,
51450         maxSize: 400,
51451         titlebar: true,
51452         collapsible: true,
51453         panels: [new CP("west", {title: "West"})]
51454     },
51455     east: {
51456         split:true,
51457         initialSize: 202,
51458         minSize: 175,
51459         maxSize: 400,
51460         titlebar: true,
51461         collapsible: true,
51462         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
51463     },
51464     south: {
51465         split:true,
51466         initialSize: 100,
51467         minSize: 100,
51468         maxSize: 200,
51469         titlebar: true,
51470         collapsible: true,
51471         panels: [new CP("south", {title: "South", closable: true})]
51472     },
51473     center: {
51474         titlebar: true,
51475         autoScroll:true,
51476         resizeTabs: true,
51477         minTabWidth: 50,
51478         preferredTabWidth: 150,
51479         panels: [
51480             new CP("center1", {title: "Close Me", closable: true}),
51481             new CP("center2", {title: "Center Panel", closable: false})
51482         ]
51483     }
51484 }, document.body);
51485
51486 layout.getRegion("center").showPanel("center1");
51487 </code></pre>
51488  * @param config
51489  * @param targetEl
51490  */
51491 Roo.BorderLayout.create = function(config, targetEl){
51492     var layout = new Roo.BorderLayout(targetEl || document.body, config);
51493     layout.beginUpdate();
51494     var regions = Roo.BorderLayout.RegionFactory.validRegions;
51495     for(var j = 0, jlen = regions.length; j < jlen; j++){
51496         var lr = regions[j];
51497         if(layout.regions[lr] && config[lr].panels){
51498             var r = layout.regions[lr];
51499             var ps = config[lr].panels;
51500             layout.addTypedPanels(r, ps);
51501         }
51502     }
51503     layout.endUpdate();
51504     return layout;
51505 };
51506
51507 // private
51508 Roo.BorderLayout.RegionFactory = {
51509     // private
51510     validRegions : ["north","south","east","west","center"],
51511
51512     // private
51513     create : function(target, mgr, config){
51514         target = target.toLowerCase();
51515         if(config.lightweight || config.basic){
51516             return new Roo.BasicLayoutRegion(mgr, config, target);
51517         }
51518         switch(target){
51519             case "north":
51520                 return new Roo.NorthLayoutRegion(mgr, config);
51521             case "south":
51522                 return new Roo.SouthLayoutRegion(mgr, config);
51523             case "east":
51524                 return new Roo.EastLayoutRegion(mgr, config);
51525             case "west":
51526                 return new Roo.WestLayoutRegion(mgr, config);
51527             case "center":
51528                 return new Roo.CenterLayoutRegion(mgr, config);
51529         }
51530         throw 'Layout region "'+target+'" not supported.';
51531     }
51532 };/*
51533  * Based on:
51534  * Ext JS Library 1.1.1
51535  * Copyright(c) 2006-2007, Ext JS, LLC.
51536  *
51537  * Originally Released Under LGPL - original licence link has changed is not relivant.
51538  *
51539  * Fork - LGPL
51540  * <script type="text/javascript">
51541  */
51542  
51543 /**
51544  * @class Roo.BasicLayoutRegion
51545  * @extends Roo.util.Observable
51546  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
51547  * and does not have a titlebar, tabs or any other features. All it does is size and position 
51548  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
51549  */
51550 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
51551     this.mgr = mgr;
51552     this.position  = pos;
51553     this.events = {
51554         /**
51555          * @scope Roo.BasicLayoutRegion
51556          */
51557         
51558         /**
51559          * @event beforeremove
51560          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
51561          * @param {Roo.LayoutRegion} this
51562          * @param {Roo.ContentPanel} panel The panel
51563          * @param {Object} e The cancel event object
51564          */
51565         "beforeremove" : true,
51566         /**
51567          * @event invalidated
51568          * Fires when the layout for this region is changed.
51569          * @param {Roo.LayoutRegion} this
51570          */
51571         "invalidated" : true,
51572         /**
51573          * @event visibilitychange
51574          * Fires when this region is shown or hidden 
51575          * @param {Roo.LayoutRegion} this
51576          * @param {Boolean} visibility true or false
51577          */
51578         "visibilitychange" : true,
51579         /**
51580          * @event paneladded
51581          * Fires when a panel is added. 
51582          * @param {Roo.LayoutRegion} this
51583          * @param {Roo.ContentPanel} panel The panel
51584          */
51585         "paneladded" : true,
51586         /**
51587          * @event panelremoved
51588          * Fires when a panel is removed. 
51589          * @param {Roo.LayoutRegion} this
51590          * @param {Roo.ContentPanel} panel The panel
51591          */
51592         "panelremoved" : true,
51593         /**
51594          * @event beforecollapse
51595          * Fires when this region before collapse.
51596          * @param {Roo.LayoutRegion} this
51597          */
51598         "beforecollapse" : true,
51599         /**
51600          * @event collapsed
51601          * Fires when this region is collapsed.
51602          * @param {Roo.LayoutRegion} this
51603          */
51604         "collapsed" : true,
51605         /**
51606          * @event expanded
51607          * Fires when this region is expanded.
51608          * @param {Roo.LayoutRegion} this
51609          */
51610         "expanded" : true,
51611         /**
51612          * @event slideshow
51613          * Fires when this region is slid into view.
51614          * @param {Roo.LayoutRegion} this
51615          */
51616         "slideshow" : true,
51617         /**
51618          * @event slidehide
51619          * Fires when this region slides out of view. 
51620          * @param {Roo.LayoutRegion} this
51621          */
51622         "slidehide" : true,
51623         /**
51624          * @event panelactivated
51625          * Fires when a panel is activated. 
51626          * @param {Roo.LayoutRegion} this
51627          * @param {Roo.ContentPanel} panel The activated panel
51628          */
51629         "panelactivated" : true,
51630         /**
51631          * @event resized
51632          * Fires when the user resizes this region. 
51633          * @param {Roo.LayoutRegion} this
51634          * @param {Number} newSize The new size (width for east/west, height for north/south)
51635          */
51636         "resized" : true
51637     };
51638     /** A collection of panels in this region. @type Roo.util.MixedCollection */
51639     this.panels = new Roo.util.MixedCollection();
51640     this.panels.getKey = this.getPanelId.createDelegate(this);
51641     this.box = null;
51642     this.activePanel = null;
51643     // ensure listeners are added...
51644     
51645     if (config.listeners || config.events) {
51646         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
51647             listeners : config.listeners || {},
51648             events : config.events || {}
51649         });
51650     }
51651     
51652     if(skipConfig !== true){
51653         this.applyConfig(config);
51654     }
51655 };
51656
51657 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
51658     getPanelId : function(p){
51659         return p.getId();
51660     },
51661     
51662     applyConfig : function(config){
51663         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51664         this.config = config;
51665         
51666     },
51667     
51668     /**
51669      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
51670      * the width, for horizontal (north, south) the height.
51671      * @param {Number} newSize The new width or height
51672      */
51673     resizeTo : function(newSize){
51674         var el = this.el ? this.el :
51675                  (this.activePanel ? this.activePanel.getEl() : null);
51676         if(el){
51677             switch(this.position){
51678                 case "east":
51679                 case "west":
51680                     el.setWidth(newSize);
51681                     this.fireEvent("resized", this, newSize);
51682                 break;
51683                 case "north":
51684                 case "south":
51685                     el.setHeight(newSize);
51686                     this.fireEvent("resized", this, newSize);
51687                 break;                
51688             }
51689         }
51690     },
51691     
51692     getBox : function(){
51693         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
51694     },
51695     
51696     getMargins : function(){
51697         return this.margins;
51698     },
51699     
51700     updateBox : function(box){
51701         this.box = box;
51702         var el = this.activePanel.getEl();
51703         el.dom.style.left = box.x + "px";
51704         el.dom.style.top = box.y + "px";
51705         this.activePanel.setSize(box.width, box.height);
51706     },
51707     
51708     /**
51709      * Returns the container element for this region.
51710      * @return {Roo.Element}
51711      */
51712     getEl : function(){
51713         return this.activePanel;
51714     },
51715     
51716     /**
51717      * Returns true if this region is currently visible.
51718      * @return {Boolean}
51719      */
51720     isVisible : function(){
51721         return this.activePanel ? true : false;
51722     },
51723     
51724     setActivePanel : function(panel){
51725         panel = this.getPanel(panel);
51726         if(this.activePanel && this.activePanel != panel){
51727             this.activePanel.setActiveState(false);
51728             this.activePanel.getEl().setLeftTop(-10000,-10000);
51729         }
51730         this.activePanel = panel;
51731         panel.setActiveState(true);
51732         if(this.box){
51733             panel.setSize(this.box.width, this.box.height);
51734         }
51735         this.fireEvent("panelactivated", this, panel);
51736         this.fireEvent("invalidated");
51737     },
51738     
51739     /**
51740      * Show the specified panel.
51741      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
51742      * @return {Roo.ContentPanel} The shown panel or null
51743      */
51744     showPanel : function(panel){
51745         if(panel = this.getPanel(panel)){
51746             this.setActivePanel(panel);
51747         }
51748         return panel;
51749     },
51750     
51751     /**
51752      * Get the active panel for this region.
51753      * @return {Roo.ContentPanel} The active panel or null
51754      */
51755     getActivePanel : function(){
51756         return this.activePanel;
51757     },
51758     
51759     /**
51760      * Add the passed ContentPanel(s)
51761      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
51762      * @return {Roo.ContentPanel} The panel added (if only one was added)
51763      */
51764     add : function(panel){
51765         if(arguments.length > 1){
51766             for(var i = 0, len = arguments.length; i < len; i++) {
51767                 this.add(arguments[i]);
51768             }
51769             return null;
51770         }
51771         if(this.hasPanel(panel)){
51772             this.showPanel(panel);
51773             return panel;
51774         }
51775         var el = panel.getEl();
51776         if(el.dom.parentNode != this.mgr.el.dom){
51777             this.mgr.el.dom.appendChild(el.dom);
51778         }
51779         if(panel.setRegion){
51780             panel.setRegion(this);
51781         }
51782         this.panels.add(panel);
51783         el.setStyle("position", "absolute");
51784         if(!panel.background){
51785             this.setActivePanel(panel);
51786             if(this.config.initialSize && this.panels.getCount()==1){
51787                 this.resizeTo(this.config.initialSize);
51788             }
51789         }
51790         this.fireEvent("paneladded", this, panel);
51791         return panel;
51792     },
51793     
51794     /**
51795      * Returns true if the panel is in this region.
51796      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51797      * @return {Boolean}
51798      */
51799     hasPanel : function(panel){
51800         if(typeof panel == "object"){ // must be panel obj
51801             panel = panel.getId();
51802         }
51803         return this.getPanel(panel) ? true : false;
51804     },
51805     
51806     /**
51807      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
51808      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51809      * @param {Boolean} preservePanel Overrides the config preservePanel option
51810      * @return {Roo.ContentPanel} The panel that was removed
51811      */
51812     remove : function(panel, preservePanel){
51813         panel = this.getPanel(panel);
51814         if(!panel){
51815             return null;
51816         }
51817         var e = {};
51818         this.fireEvent("beforeremove", this, panel, e);
51819         if(e.cancel === true){
51820             return null;
51821         }
51822         var panelId = panel.getId();
51823         this.panels.removeKey(panelId);
51824         return panel;
51825     },
51826     
51827     /**
51828      * Returns the panel specified or null if it's not in this region.
51829      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51830      * @return {Roo.ContentPanel}
51831      */
51832     getPanel : function(id){
51833         if(typeof id == "object"){ // must be panel obj
51834             return id;
51835         }
51836         return this.panels.get(id);
51837     },
51838     
51839     /**
51840      * Returns this regions position (north/south/east/west/center).
51841      * @return {String} 
51842      */
51843     getPosition: function(){
51844         return this.position;    
51845     }
51846 });/*
51847  * Based on:
51848  * Ext JS Library 1.1.1
51849  * Copyright(c) 2006-2007, Ext JS, LLC.
51850  *
51851  * Originally Released Under LGPL - original licence link has changed is not relivant.
51852  *
51853  * Fork - LGPL
51854  * <script type="text/javascript">
51855  */
51856  
51857 /**
51858  * @class Roo.LayoutRegion
51859  * @extends Roo.BasicLayoutRegion
51860  * This class represents a region in a layout manager.
51861  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
51862  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
51863  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
51864  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
51865  * @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})
51866  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
51867  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
51868  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
51869  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
51870  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
51871  * @cfg {String}    title           The title for the region (overrides panel titles)
51872  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
51873  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
51874  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
51875  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
51876  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
51877  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
51878  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
51879  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
51880  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
51881  * @cfg {Boolean}   showPin         True to show a pin button
51882  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
51883  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
51884  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
51885  * @cfg {Number}    width           For East/West panels
51886  * @cfg {Number}    height          For North/South panels
51887  * @cfg {Boolean}   split           To show the splitter
51888  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
51889  */
51890 Roo.LayoutRegion = function(mgr, config, pos){
51891     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
51892     var dh = Roo.DomHelper;
51893     /** This region's container element 
51894     * @type Roo.Element */
51895     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
51896     /** This region's title element 
51897     * @type Roo.Element */
51898
51899     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
51900         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
51901         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
51902     ]}, true);
51903     this.titleEl.enableDisplayMode();
51904     /** This region's title text element 
51905     * @type HTMLElement */
51906     this.titleTextEl = this.titleEl.dom.firstChild;
51907     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
51908     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
51909     this.closeBtn.enableDisplayMode();
51910     this.closeBtn.on("click", this.closeClicked, this);
51911     this.closeBtn.hide();
51912
51913     this.createBody(config);
51914     this.visible = true;
51915     this.collapsed = false;
51916
51917     if(config.hideWhenEmpty){
51918         this.hide();
51919         this.on("paneladded", this.validateVisibility, this);
51920         this.on("panelremoved", this.validateVisibility, this);
51921     }
51922     this.applyConfig(config);
51923 };
51924
51925 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
51926
51927     createBody : function(){
51928         /** This region's body element 
51929         * @type Roo.Element */
51930         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
51931     },
51932
51933     applyConfig : function(c){
51934         if(c.collapsible && this.position != "center" && !this.collapsedEl){
51935             var dh = Roo.DomHelper;
51936             if(c.titlebar !== false){
51937                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
51938                 this.collapseBtn.on("click", this.collapse, this);
51939                 this.collapseBtn.enableDisplayMode();
51940
51941                 if(c.showPin === true || this.showPin){
51942                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
51943                     this.stickBtn.enableDisplayMode();
51944                     this.stickBtn.on("click", this.expand, this);
51945                     this.stickBtn.hide();
51946                 }
51947             }
51948             /** This region's collapsed element
51949             * @type Roo.Element */
51950             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
51951                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
51952             ]}, true);
51953             if(c.floatable !== false){
51954                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
51955                this.collapsedEl.on("click", this.collapseClick, this);
51956             }
51957
51958             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
51959                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
51960                    id: "message", unselectable: "on", style:{"float":"left"}});
51961                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
51962              }
51963             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
51964             this.expandBtn.on("click", this.expand, this);
51965         }
51966         if(this.collapseBtn){
51967             this.collapseBtn.setVisible(c.collapsible == true);
51968         }
51969         this.cmargins = c.cmargins || this.cmargins ||
51970                          (this.position == "west" || this.position == "east" ?
51971                              {top: 0, left: 2, right:2, bottom: 0} :
51972                              {top: 2, left: 0, right:0, bottom: 2});
51973         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51974         this.bottomTabs = c.tabPosition != "top";
51975         this.autoScroll = c.autoScroll || false;
51976         if(this.autoScroll){
51977             this.bodyEl.setStyle("overflow", "auto");
51978         }else{
51979             this.bodyEl.setStyle("overflow", "hidden");
51980         }
51981         //if(c.titlebar !== false){
51982             if((!c.titlebar && !c.title) || c.titlebar === false){
51983                 this.titleEl.hide();
51984             }else{
51985                 this.titleEl.show();
51986                 if(c.title){
51987                     this.titleTextEl.innerHTML = c.title;
51988                 }
51989             }
51990         //}
51991         this.duration = c.duration || .30;
51992         this.slideDuration = c.slideDuration || .45;
51993         this.config = c;
51994         if(c.collapsed){
51995             this.collapse(true);
51996         }
51997         if(c.hidden){
51998             this.hide();
51999         }
52000     },
52001     /**
52002      * Returns true if this region is currently visible.
52003      * @return {Boolean}
52004      */
52005     isVisible : function(){
52006         return this.visible;
52007     },
52008
52009     /**
52010      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
52011      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
52012      */
52013     setCollapsedTitle : function(title){
52014         title = title || "&#160;";
52015         if(this.collapsedTitleTextEl){
52016             this.collapsedTitleTextEl.innerHTML = title;
52017         }
52018     },
52019
52020     getBox : function(){
52021         var b;
52022         if(!this.collapsed){
52023             b = this.el.getBox(false, true);
52024         }else{
52025             b = this.collapsedEl.getBox(false, true);
52026         }
52027         return b;
52028     },
52029
52030     getMargins : function(){
52031         return this.collapsed ? this.cmargins : this.margins;
52032     },
52033
52034     highlight : function(){
52035         this.el.addClass("x-layout-panel-dragover");
52036     },
52037
52038     unhighlight : function(){
52039         this.el.removeClass("x-layout-panel-dragover");
52040     },
52041
52042     updateBox : function(box){
52043         this.box = box;
52044         if(!this.collapsed){
52045             this.el.dom.style.left = box.x + "px";
52046             this.el.dom.style.top = box.y + "px";
52047             this.updateBody(box.width, box.height);
52048         }else{
52049             this.collapsedEl.dom.style.left = box.x + "px";
52050             this.collapsedEl.dom.style.top = box.y + "px";
52051             this.collapsedEl.setSize(box.width, box.height);
52052         }
52053         if(this.tabs){
52054             this.tabs.autoSizeTabs();
52055         }
52056     },
52057
52058     updateBody : function(w, h){
52059         if(w !== null){
52060             this.el.setWidth(w);
52061             w -= this.el.getBorderWidth("rl");
52062             if(this.config.adjustments){
52063                 w += this.config.adjustments[0];
52064             }
52065         }
52066         if(h !== null){
52067             this.el.setHeight(h);
52068             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
52069             h -= this.el.getBorderWidth("tb");
52070             if(this.config.adjustments){
52071                 h += this.config.adjustments[1];
52072             }
52073             this.bodyEl.setHeight(h);
52074             if(this.tabs){
52075                 h = this.tabs.syncHeight(h);
52076             }
52077         }
52078         if(this.panelSize){
52079             w = w !== null ? w : this.panelSize.width;
52080             h = h !== null ? h : this.panelSize.height;
52081         }
52082         if(this.activePanel){
52083             var el = this.activePanel.getEl();
52084             w = w !== null ? w : el.getWidth();
52085             h = h !== null ? h : el.getHeight();
52086             this.panelSize = {width: w, height: h};
52087             this.activePanel.setSize(w, h);
52088         }
52089         if(Roo.isIE && this.tabs){
52090             this.tabs.el.repaint();
52091         }
52092     },
52093
52094     /**
52095      * Returns the container element for this region.
52096      * @return {Roo.Element}
52097      */
52098     getEl : function(){
52099         return this.el;
52100     },
52101
52102     /**
52103      * Hides this region.
52104      */
52105     hide : function(){
52106         if(!this.collapsed){
52107             this.el.dom.style.left = "-2000px";
52108             this.el.hide();
52109         }else{
52110             this.collapsedEl.dom.style.left = "-2000px";
52111             this.collapsedEl.hide();
52112         }
52113         this.visible = false;
52114         this.fireEvent("visibilitychange", this, false);
52115     },
52116
52117     /**
52118      * Shows this region if it was previously hidden.
52119      */
52120     show : function(){
52121         if(!this.collapsed){
52122             this.el.show();
52123         }else{
52124             this.collapsedEl.show();
52125         }
52126         this.visible = true;
52127         this.fireEvent("visibilitychange", this, true);
52128     },
52129
52130     closeClicked : function(){
52131         if(this.activePanel){
52132             this.remove(this.activePanel);
52133         }
52134     },
52135
52136     collapseClick : function(e){
52137         if(this.isSlid){
52138            e.stopPropagation();
52139            this.slideIn();
52140         }else{
52141            e.stopPropagation();
52142            this.slideOut();
52143         }
52144     },
52145
52146     /**
52147      * Collapses this region.
52148      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
52149      */
52150     collapse : function(skipAnim, skipCheck = false){
52151         if(this.collapsed) {
52152             return;
52153         }
52154         
52155         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
52156             
52157             this.collapsed = true;
52158             if(this.split){
52159                 this.split.el.hide();
52160             }
52161             if(this.config.animate && skipAnim !== true){
52162                 this.fireEvent("invalidated", this);
52163                 this.animateCollapse();
52164             }else{
52165                 this.el.setLocation(-20000,-20000);
52166                 this.el.hide();
52167                 this.collapsedEl.show();
52168                 this.fireEvent("collapsed", this);
52169                 this.fireEvent("invalidated", this);
52170             }
52171         }
52172         
52173     },
52174
52175     animateCollapse : function(){
52176         // overridden
52177     },
52178
52179     /**
52180      * Expands this region if it was previously collapsed.
52181      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
52182      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
52183      */
52184     expand : function(e, skipAnim){
52185         if(e) {
52186             e.stopPropagation();
52187         }
52188         if(!this.collapsed || this.el.hasActiveFx()) {
52189             return;
52190         }
52191         if(this.isSlid){
52192             this.afterSlideIn();
52193             skipAnim = true;
52194         }
52195         this.collapsed = false;
52196         if(this.config.animate && skipAnim !== true){
52197             this.animateExpand();
52198         }else{
52199             this.el.show();
52200             if(this.split){
52201                 this.split.el.show();
52202             }
52203             this.collapsedEl.setLocation(-2000,-2000);
52204             this.collapsedEl.hide();
52205             this.fireEvent("invalidated", this);
52206             this.fireEvent("expanded", this);
52207         }
52208     },
52209
52210     animateExpand : function(){
52211         // overridden
52212     },
52213
52214     initTabs : function()
52215     {
52216         this.bodyEl.setStyle("overflow", "hidden");
52217         var ts = new Roo.TabPanel(
52218                 this.bodyEl.dom,
52219                 {
52220                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
52221                     disableTooltips: this.config.disableTabTips,
52222                     toolbar : this.config.toolbar
52223                 }
52224         );
52225         if(this.config.hideTabs){
52226             ts.stripWrap.setDisplayed(false);
52227         }
52228         this.tabs = ts;
52229         ts.resizeTabs = this.config.resizeTabs === true;
52230         ts.minTabWidth = this.config.minTabWidth || 40;
52231         ts.maxTabWidth = this.config.maxTabWidth || 250;
52232         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
52233         ts.monitorResize = false;
52234         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52235         ts.bodyEl.addClass('x-layout-tabs-body');
52236         this.panels.each(this.initPanelAsTab, this);
52237     },
52238
52239     initPanelAsTab : function(panel){
52240         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
52241                     this.config.closeOnTab && panel.isClosable());
52242         if(panel.tabTip !== undefined){
52243             ti.setTooltip(panel.tabTip);
52244         }
52245         ti.on("activate", function(){
52246               this.setActivePanel(panel);
52247         }, this);
52248         if(this.config.closeOnTab){
52249             ti.on("beforeclose", function(t, e){
52250                 e.cancel = true;
52251                 this.remove(panel);
52252             }, this);
52253         }
52254         return ti;
52255     },
52256
52257     updatePanelTitle : function(panel, title){
52258         if(this.activePanel == panel){
52259             this.updateTitle(title);
52260         }
52261         if(this.tabs){
52262             var ti = this.tabs.getTab(panel.getEl().id);
52263             ti.setText(title);
52264             if(panel.tabTip !== undefined){
52265                 ti.setTooltip(panel.tabTip);
52266             }
52267         }
52268     },
52269
52270     updateTitle : function(title){
52271         if(this.titleTextEl && !this.config.title){
52272             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
52273         }
52274     },
52275
52276     setActivePanel : function(panel){
52277         panel = this.getPanel(panel);
52278         if(this.activePanel && this.activePanel != panel){
52279             this.activePanel.setActiveState(false);
52280         }
52281         this.activePanel = panel;
52282         panel.setActiveState(true);
52283         if(this.panelSize){
52284             panel.setSize(this.panelSize.width, this.panelSize.height);
52285         }
52286         if(this.closeBtn){
52287             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
52288         }
52289         this.updateTitle(panel.getTitle());
52290         if(this.tabs){
52291             this.fireEvent("invalidated", this);
52292         }
52293         this.fireEvent("panelactivated", this, panel);
52294     },
52295
52296     /**
52297      * Shows the specified panel.
52298      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
52299      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
52300      */
52301     showPanel : function(panel)
52302     {
52303         panel = this.getPanel(panel);
52304         if(panel){
52305             if(this.tabs){
52306                 var tab = this.tabs.getTab(panel.getEl().id);
52307                 if(tab.isHidden()){
52308                     this.tabs.unhideTab(tab.id);
52309                 }
52310                 tab.activate();
52311             }else{
52312                 this.setActivePanel(panel);
52313             }
52314         }
52315         return panel;
52316     },
52317
52318     /**
52319      * Get the active panel for this region.
52320      * @return {Roo.ContentPanel} The active panel or null
52321      */
52322     getActivePanel : function(){
52323         return this.activePanel;
52324     },
52325
52326     validateVisibility : function(){
52327         if(this.panels.getCount() < 1){
52328             this.updateTitle("&#160;");
52329             this.closeBtn.hide();
52330             this.hide();
52331         }else{
52332             if(!this.isVisible()){
52333                 this.show();
52334             }
52335         }
52336     },
52337
52338     /**
52339      * Adds the passed ContentPanel(s) to this region.
52340      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52341      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
52342      */
52343     add : function(panel){
52344         if(arguments.length > 1){
52345             for(var i = 0, len = arguments.length; i < len; i++) {
52346                 this.add(arguments[i]);
52347             }
52348             return null;
52349         }
52350         if(this.hasPanel(panel)){
52351             this.showPanel(panel);
52352             return panel;
52353         }
52354         panel.setRegion(this);
52355         this.panels.add(panel);
52356         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
52357             this.bodyEl.dom.appendChild(panel.getEl().dom);
52358             if(panel.background !== true){
52359                 this.setActivePanel(panel);
52360             }
52361             this.fireEvent("paneladded", this, panel);
52362             return panel;
52363         }
52364         if(!this.tabs){
52365             this.initTabs();
52366         }else{
52367             this.initPanelAsTab(panel);
52368         }
52369         if(panel.background !== true){
52370             this.tabs.activate(panel.getEl().id);
52371         }
52372         this.fireEvent("paneladded", this, panel);
52373         return panel;
52374     },
52375
52376     /**
52377      * Hides the tab for the specified panel.
52378      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52379      */
52380     hidePanel : function(panel){
52381         if(this.tabs && (panel = this.getPanel(panel))){
52382             this.tabs.hideTab(panel.getEl().id);
52383         }
52384     },
52385
52386     /**
52387      * Unhides the tab for a previously hidden panel.
52388      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52389      */
52390     unhidePanel : function(panel){
52391         if(this.tabs && (panel = this.getPanel(panel))){
52392             this.tabs.unhideTab(panel.getEl().id);
52393         }
52394     },
52395
52396     clearPanels : function(){
52397         while(this.panels.getCount() > 0){
52398              this.remove(this.panels.first());
52399         }
52400     },
52401
52402     /**
52403      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52404      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52405      * @param {Boolean} preservePanel Overrides the config preservePanel option
52406      * @return {Roo.ContentPanel} The panel that was removed
52407      */
52408     remove : function(panel, preservePanel){
52409         panel = this.getPanel(panel);
52410         if(!panel){
52411             return null;
52412         }
52413         var e = {};
52414         this.fireEvent("beforeremove", this, panel, e);
52415         if(e.cancel === true){
52416             return null;
52417         }
52418         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
52419         var panelId = panel.getId();
52420         this.panels.removeKey(panelId);
52421         if(preservePanel){
52422             document.body.appendChild(panel.getEl().dom);
52423         }
52424         if(this.tabs){
52425             this.tabs.removeTab(panel.getEl().id);
52426         }else if (!preservePanel){
52427             this.bodyEl.dom.removeChild(panel.getEl().dom);
52428         }
52429         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
52430             var p = this.panels.first();
52431             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
52432             tempEl.appendChild(p.getEl().dom);
52433             this.bodyEl.update("");
52434             this.bodyEl.dom.appendChild(p.getEl().dom);
52435             tempEl = null;
52436             this.updateTitle(p.getTitle());
52437             this.tabs = null;
52438             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52439             this.setActivePanel(p);
52440         }
52441         panel.setRegion(null);
52442         if(this.activePanel == panel){
52443             this.activePanel = null;
52444         }
52445         if(this.config.autoDestroy !== false && preservePanel !== true){
52446             try{panel.destroy();}catch(e){}
52447         }
52448         this.fireEvent("panelremoved", this, panel);
52449         return panel;
52450     },
52451
52452     /**
52453      * Returns the TabPanel component used by this region
52454      * @return {Roo.TabPanel}
52455      */
52456     getTabs : function(){
52457         return this.tabs;
52458     },
52459
52460     createTool : function(parentEl, className){
52461         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
52462             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
52463         btn.addClassOnOver("x-layout-tools-button-over");
52464         return btn;
52465     }
52466 });/*
52467  * Based on:
52468  * Ext JS Library 1.1.1
52469  * Copyright(c) 2006-2007, Ext JS, LLC.
52470  *
52471  * Originally Released Under LGPL - original licence link has changed is not relivant.
52472  *
52473  * Fork - LGPL
52474  * <script type="text/javascript">
52475  */
52476  
52477
52478
52479 /**
52480  * @class Roo.SplitLayoutRegion
52481  * @extends Roo.LayoutRegion
52482  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
52483  */
52484 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
52485     this.cursor = cursor;
52486     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
52487 };
52488
52489 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
52490     splitTip : "Drag to resize.",
52491     collapsibleSplitTip : "Drag to resize. Double click to hide.",
52492     useSplitTips : false,
52493
52494     applyConfig : function(config){
52495         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
52496         if(config.split){
52497             if(!this.split){
52498                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
52499                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
52500                 /** The SplitBar for this region 
52501                 * @type Roo.SplitBar */
52502                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
52503                 this.split.on("moved", this.onSplitMove, this);
52504                 this.split.useShim = config.useShim === true;
52505                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
52506                 if(this.useSplitTips){
52507                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
52508                 }
52509                 if(config.collapsible){
52510                     this.split.el.on("dblclick", this.collapse,  this);
52511                 }
52512             }
52513             if(typeof config.minSize != "undefined"){
52514                 this.split.minSize = config.minSize;
52515             }
52516             if(typeof config.maxSize != "undefined"){
52517                 this.split.maxSize = config.maxSize;
52518             }
52519             if(config.hideWhenEmpty || config.hidden || config.collapsed){
52520                 this.hideSplitter();
52521             }
52522         }
52523     },
52524
52525     getHMaxSize : function(){
52526          var cmax = this.config.maxSize || 10000;
52527          var center = this.mgr.getRegion("center");
52528          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
52529     },
52530
52531     getVMaxSize : function(){
52532          var cmax = this.config.maxSize || 10000;
52533          var center = this.mgr.getRegion("center");
52534          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
52535     },
52536
52537     onSplitMove : function(split, newSize){
52538         this.fireEvent("resized", this, newSize);
52539     },
52540     
52541     /** 
52542      * Returns the {@link Roo.SplitBar} for this region.
52543      * @return {Roo.SplitBar}
52544      */
52545     getSplitBar : function(){
52546         return this.split;
52547     },
52548     
52549     hide : function(){
52550         this.hideSplitter();
52551         Roo.SplitLayoutRegion.superclass.hide.call(this);
52552     },
52553
52554     hideSplitter : function(){
52555         if(this.split){
52556             this.split.el.setLocation(-2000,-2000);
52557             this.split.el.hide();
52558         }
52559     },
52560
52561     show : function(){
52562         if(this.split){
52563             this.split.el.show();
52564         }
52565         Roo.SplitLayoutRegion.superclass.show.call(this);
52566     },
52567     
52568     beforeSlide: function(){
52569         if(Roo.isGecko){// firefox overflow auto bug workaround
52570             this.bodyEl.clip();
52571             if(this.tabs) {
52572                 this.tabs.bodyEl.clip();
52573             }
52574             if(this.activePanel){
52575                 this.activePanel.getEl().clip();
52576                 
52577                 if(this.activePanel.beforeSlide){
52578                     this.activePanel.beforeSlide();
52579                 }
52580             }
52581         }
52582     },
52583     
52584     afterSlide : function(){
52585         if(Roo.isGecko){// firefox overflow auto bug workaround
52586             this.bodyEl.unclip();
52587             if(this.tabs) {
52588                 this.tabs.bodyEl.unclip();
52589             }
52590             if(this.activePanel){
52591                 this.activePanel.getEl().unclip();
52592                 if(this.activePanel.afterSlide){
52593                     this.activePanel.afterSlide();
52594                 }
52595             }
52596         }
52597     },
52598
52599     initAutoHide : function(){
52600         if(this.autoHide !== false){
52601             if(!this.autoHideHd){
52602                 var st = new Roo.util.DelayedTask(this.slideIn, this);
52603                 this.autoHideHd = {
52604                     "mouseout": function(e){
52605                         if(!e.within(this.el, true)){
52606                             st.delay(500);
52607                         }
52608                     },
52609                     "mouseover" : function(e){
52610                         st.cancel();
52611                     },
52612                     scope : this
52613                 };
52614             }
52615             this.el.on(this.autoHideHd);
52616         }
52617     },
52618
52619     clearAutoHide : function(){
52620         if(this.autoHide !== false){
52621             this.el.un("mouseout", this.autoHideHd.mouseout);
52622             this.el.un("mouseover", this.autoHideHd.mouseover);
52623         }
52624     },
52625
52626     clearMonitor : function(){
52627         Roo.get(document).un("click", this.slideInIf, this);
52628     },
52629
52630     // these names are backwards but not changed for compat
52631     slideOut : function(){
52632         if(this.isSlid || this.el.hasActiveFx()){
52633             return;
52634         }
52635         this.isSlid = true;
52636         if(this.collapseBtn){
52637             this.collapseBtn.hide();
52638         }
52639         this.closeBtnState = this.closeBtn.getStyle('display');
52640         this.closeBtn.hide();
52641         if(this.stickBtn){
52642             this.stickBtn.show();
52643         }
52644         this.el.show();
52645         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
52646         this.beforeSlide();
52647         this.el.setStyle("z-index", 10001);
52648         this.el.slideIn(this.getSlideAnchor(), {
52649             callback: function(){
52650                 this.afterSlide();
52651                 this.initAutoHide();
52652                 Roo.get(document).on("click", this.slideInIf, this);
52653                 this.fireEvent("slideshow", this);
52654             },
52655             scope: this,
52656             block: true
52657         });
52658     },
52659
52660     afterSlideIn : function(){
52661         this.clearAutoHide();
52662         this.isSlid = false;
52663         this.clearMonitor();
52664         this.el.setStyle("z-index", "");
52665         if(this.collapseBtn){
52666             this.collapseBtn.show();
52667         }
52668         this.closeBtn.setStyle('display', this.closeBtnState);
52669         if(this.stickBtn){
52670             this.stickBtn.hide();
52671         }
52672         this.fireEvent("slidehide", this);
52673     },
52674
52675     slideIn : function(cb){
52676         if(!this.isSlid || this.el.hasActiveFx()){
52677             Roo.callback(cb);
52678             return;
52679         }
52680         this.isSlid = false;
52681         this.beforeSlide();
52682         this.el.slideOut(this.getSlideAnchor(), {
52683             callback: function(){
52684                 this.el.setLeftTop(-10000, -10000);
52685                 this.afterSlide();
52686                 this.afterSlideIn();
52687                 Roo.callback(cb);
52688             },
52689             scope: this,
52690             block: true
52691         });
52692     },
52693     
52694     slideInIf : function(e){
52695         if(!e.within(this.el)){
52696             this.slideIn();
52697         }
52698     },
52699
52700     animateCollapse : function(){
52701         this.beforeSlide();
52702         this.el.setStyle("z-index", 20000);
52703         var anchor = this.getSlideAnchor();
52704         this.el.slideOut(anchor, {
52705             callback : function(){
52706                 this.el.setStyle("z-index", "");
52707                 this.collapsedEl.slideIn(anchor, {duration:.3});
52708                 this.afterSlide();
52709                 this.el.setLocation(-10000,-10000);
52710                 this.el.hide();
52711                 this.fireEvent("collapsed", this);
52712             },
52713             scope: this,
52714             block: true
52715         });
52716     },
52717
52718     animateExpand : function(){
52719         this.beforeSlide();
52720         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
52721         this.el.setStyle("z-index", 20000);
52722         this.collapsedEl.hide({
52723             duration:.1
52724         });
52725         this.el.slideIn(this.getSlideAnchor(), {
52726             callback : function(){
52727                 this.el.setStyle("z-index", "");
52728                 this.afterSlide();
52729                 if(this.split){
52730                     this.split.el.show();
52731                 }
52732                 this.fireEvent("invalidated", this);
52733                 this.fireEvent("expanded", this);
52734             },
52735             scope: this,
52736             block: true
52737         });
52738     },
52739
52740     anchors : {
52741         "west" : "left",
52742         "east" : "right",
52743         "north" : "top",
52744         "south" : "bottom"
52745     },
52746
52747     sanchors : {
52748         "west" : "l",
52749         "east" : "r",
52750         "north" : "t",
52751         "south" : "b"
52752     },
52753
52754     canchors : {
52755         "west" : "tl-tr",
52756         "east" : "tr-tl",
52757         "north" : "tl-bl",
52758         "south" : "bl-tl"
52759     },
52760
52761     getAnchor : function(){
52762         return this.anchors[this.position];
52763     },
52764
52765     getCollapseAnchor : function(){
52766         return this.canchors[this.position];
52767     },
52768
52769     getSlideAnchor : function(){
52770         return this.sanchors[this.position];
52771     },
52772
52773     getAlignAdj : function(){
52774         var cm = this.cmargins;
52775         switch(this.position){
52776             case "west":
52777                 return [0, 0];
52778             break;
52779             case "east":
52780                 return [0, 0];
52781             break;
52782             case "north":
52783                 return [0, 0];
52784             break;
52785             case "south":
52786                 return [0, 0];
52787             break;
52788         }
52789     },
52790
52791     getExpandAdj : function(){
52792         var c = this.collapsedEl, cm = this.cmargins;
52793         switch(this.position){
52794             case "west":
52795                 return [-(cm.right+c.getWidth()+cm.left), 0];
52796             break;
52797             case "east":
52798                 return [cm.right+c.getWidth()+cm.left, 0];
52799             break;
52800             case "north":
52801                 return [0, -(cm.top+cm.bottom+c.getHeight())];
52802             break;
52803             case "south":
52804                 return [0, cm.top+cm.bottom+c.getHeight()];
52805             break;
52806         }
52807     }
52808 });/*
52809  * Based on:
52810  * Ext JS Library 1.1.1
52811  * Copyright(c) 2006-2007, Ext JS, LLC.
52812  *
52813  * Originally Released Under LGPL - original licence link has changed is not relivant.
52814  *
52815  * Fork - LGPL
52816  * <script type="text/javascript">
52817  */
52818 /*
52819  * These classes are private internal classes
52820  */
52821 Roo.CenterLayoutRegion = function(mgr, config){
52822     Roo.LayoutRegion.call(this, mgr, config, "center");
52823     this.visible = true;
52824     this.minWidth = config.minWidth || 20;
52825     this.minHeight = config.minHeight || 20;
52826 };
52827
52828 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
52829     hide : function(){
52830         // center panel can't be hidden
52831     },
52832     
52833     show : function(){
52834         // center panel can't be hidden
52835     },
52836     
52837     getMinWidth: function(){
52838         return this.minWidth;
52839     },
52840     
52841     getMinHeight: function(){
52842         return this.minHeight;
52843     }
52844 });
52845
52846
52847 Roo.NorthLayoutRegion = function(mgr, config){
52848     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
52849     if(this.split){
52850         this.split.placement = Roo.SplitBar.TOP;
52851         this.split.orientation = Roo.SplitBar.VERTICAL;
52852         this.split.el.addClass("x-layout-split-v");
52853     }
52854     var size = config.initialSize || config.height;
52855     if(typeof size != "undefined"){
52856         this.el.setHeight(size);
52857     }
52858 };
52859 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
52860     orientation: Roo.SplitBar.VERTICAL,
52861     getBox : function(){
52862         if(this.collapsed){
52863             return this.collapsedEl.getBox();
52864         }
52865         var box = this.el.getBox();
52866         if(this.split){
52867             box.height += this.split.el.getHeight();
52868         }
52869         return box;
52870     },
52871     
52872     updateBox : function(box){
52873         if(this.split && !this.collapsed){
52874             box.height -= this.split.el.getHeight();
52875             this.split.el.setLeft(box.x);
52876             this.split.el.setTop(box.y+box.height);
52877             this.split.el.setWidth(box.width);
52878         }
52879         if(this.collapsed){
52880             this.updateBody(box.width, null);
52881         }
52882         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52883     }
52884 });
52885
52886 Roo.SouthLayoutRegion = function(mgr, config){
52887     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
52888     if(this.split){
52889         this.split.placement = Roo.SplitBar.BOTTOM;
52890         this.split.orientation = Roo.SplitBar.VERTICAL;
52891         this.split.el.addClass("x-layout-split-v");
52892     }
52893     var size = config.initialSize || config.height;
52894     if(typeof size != "undefined"){
52895         this.el.setHeight(size);
52896     }
52897 };
52898 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
52899     orientation: Roo.SplitBar.VERTICAL,
52900     getBox : function(){
52901         if(this.collapsed){
52902             return this.collapsedEl.getBox();
52903         }
52904         var box = this.el.getBox();
52905         if(this.split){
52906             var sh = this.split.el.getHeight();
52907             box.height += sh;
52908             box.y -= sh;
52909         }
52910         return box;
52911     },
52912     
52913     updateBox : function(box){
52914         if(this.split && !this.collapsed){
52915             var sh = this.split.el.getHeight();
52916             box.height -= sh;
52917             box.y += sh;
52918             this.split.el.setLeft(box.x);
52919             this.split.el.setTop(box.y-sh);
52920             this.split.el.setWidth(box.width);
52921         }
52922         if(this.collapsed){
52923             this.updateBody(box.width, null);
52924         }
52925         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52926     }
52927 });
52928
52929 Roo.EastLayoutRegion = function(mgr, config){
52930     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
52931     if(this.split){
52932         this.split.placement = Roo.SplitBar.RIGHT;
52933         this.split.orientation = Roo.SplitBar.HORIZONTAL;
52934         this.split.el.addClass("x-layout-split-h");
52935     }
52936     var size = config.initialSize || config.width;
52937     if(typeof size != "undefined"){
52938         this.el.setWidth(size);
52939     }
52940 };
52941 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
52942     orientation: Roo.SplitBar.HORIZONTAL,
52943     getBox : function(){
52944         if(this.collapsed){
52945             return this.collapsedEl.getBox();
52946         }
52947         var box = this.el.getBox();
52948         if(this.split){
52949             var sw = this.split.el.getWidth();
52950             box.width += sw;
52951             box.x -= sw;
52952         }
52953         return box;
52954     },
52955
52956     updateBox : function(box){
52957         if(this.split && !this.collapsed){
52958             var sw = this.split.el.getWidth();
52959             box.width -= sw;
52960             this.split.el.setLeft(box.x);
52961             this.split.el.setTop(box.y);
52962             this.split.el.setHeight(box.height);
52963             box.x += sw;
52964         }
52965         if(this.collapsed){
52966             this.updateBody(null, box.height);
52967         }
52968         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52969     }
52970 });
52971
52972 Roo.WestLayoutRegion = function(mgr, config){
52973     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
52974     if(this.split){
52975         this.split.placement = Roo.SplitBar.LEFT;
52976         this.split.orientation = Roo.SplitBar.HORIZONTAL;
52977         this.split.el.addClass("x-layout-split-h");
52978     }
52979     var size = config.initialSize || config.width;
52980     if(typeof size != "undefined"){
52981         this.el.setWidth(size);
52982     }
52983 };
52984 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
52985     orientation: Roo.SplitBar.HORIZONTAL,
52986     getBox : function(){
52987         if(this.collapsed){
52988             return this.collapsedEl.getBox();
52989         }
52990         var box = this.el.getBox();
52991         if(this.split){
52992             box.width += this.split.el.getWidth();
52993         }
52994         return box;
52995     },
52996     
52997     updateBox : function(box){
52998         if(this.split && !this.collapsed){
52999             var sw = this.split.el.getWidth();
53000             box.width -= sw;
53001             this.split.el.setLeft(box.x+box.width);
53002             this.split.el.setTop(box.y);
53003             this.split.el.setHeight(box.height);
53004         }
53005         if(this.collapsed){
53006             this.updateBody(null, box.height);
53007         }
53008         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53009     }
53010 });
53011 /*
53012  * Based on:
53013  * Ext JS Library 1.1.1
53014  * Copyright(c) 2006-2007, Ext JS, LLC.
53015  *
53016  * Originally Released Under LGPL - original licence link has changed is not relivant.
53017  *
53018  * Fork - LGPL
53019  * <script type="text/javascript">
53020  */
53021  
53022  
53023 /*
53024  * Private internal class for reading and applying state
53025  */
53026 Roo.LayoutStateManager = function(layout){
53027      // default empty state
53028      this.state = {
53029         north: {},
53030         south: {},
53031         east: {},
53032         west: {}       
53033     };
53034 };
53035
53036 Roo.LayoutStateManager.prototype = {
53037     init : function(layout, provider){
53038         this.provider = provider;
53039         var state = provider.get(layout.id+"-layout-state");
53040         if(state){
53041             var wasUpdating = layout.isUpdating();
53042             if(!wasUpdating){
53043                 layout.beginUpdate();
53044             }
53045             for(var key in state){
53046                 if(typeof state[key] != "function"){
53047                     var rstate = state[key];
53048                     var r = layout.getRegion(key);
53049                     if(r && rstate){
53050                         if(rstate.size){
53051                             r.resizeTo(rstate.size);
53052                         }
53053                         if(rstate.collapsed == true){
53054                             r.collapse(true);
53055                         }else{
53056                             r.expand(null, true);
53057                         }
53058                     }
53059                 }
53060             }
53061             if(!wasUpdating){
53062                 layout.endUpdate();
53063             }
53064             this.state = state; 
53065         }
53066         this.layout = layout;
53067         layout.on("regionresized", this.onRegionResized, this);
53068         layout.on("regioncollapsed", this.onRegionCollapsed, this);
53069         layout.on("regionexpanded", this.onRegionExpanded, this);
53070     },
53071     
53072     storeState : function(){
53073         this.provider.set(this.layout.id+"-layout-state", this.state);
53074     },
53075     
53076     onRegionResized : function(region, newSize){
53077         this.state[region.getPosition()].size = newSize;
53078         this.storeState();
53079     },
53080     
53081     onRegionCollapsed : function(region){
53082         this.state[region.getPosition()].collapsed = true;
53083         this.storeState();
53084     },
53085     
53086     onRegionExpanded : function(region){
53087         this.state[region.getPosition()].collapsed = false;
53088         this.storeState();
53089     }
53090 };/*
53091  * Based on:
53092  * Ext JS Library 1.1.1
53093  * Copyright(c) 2006-2007, Ext JS, LLC.
53094  *
53095  * Originally Released Under LGPL - original licence link has changed is not relivant.
53096  *
53097  * Fork - LGPL
53098  * <script type="text/javascript">
53099  */
53100 /**
53101  * @class Roo.ContentPanel
53102  * @extends Roo.util.Observable
53103  * A basic ContentPanel element.
53104  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
53105  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
53106  * @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
53107  * @cfg {Boolean}   closable      True if the panel can be closed/removed
53108  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
53109  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
53110  * @cfg {Toolbar}   toolbar       A toolbar for this panel
53111  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
53112  * @cfg {String} title          The title for this panel
53113  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
53114  * @cfg {String} url            Calls {@link #setUrl} with this value
53115  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
53116  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
53117  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
53118  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
53119
53120  * @constructor
53121  * Create a new ContentPanel.
53122  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
53123  * @param {String/Object} config A string to set only the title or a config object
53124  * @param {String} content (optional) Set the HTML content for this panel
53125  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
53126  */
53127 Roo.ContentPanel = function(el, config, content){
53128     
53129      
53130     /*
53131     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
53132         config = el;
53133         el = Roo.id();
53134     }
53135     if (config && config.parentLayout) { 
53136         el = config.parentLayout.el.createChild(); 
53137     }
53138     */
53139     if(el.autoCreate){ // xtype is available if this is called from factory
53140         config = el;
53141         el = Roo.id();
53142     }
53143     this.el = Roo.get(el);
53144     if(!this.el && config && config.autoCreate){
53145         if(typeof config.autoCreate == "object"){
53146             if(!config.autoCreate.id){
53147                 config.autoCreate.id = config.id||el;
53148             }
53149             this.el = Roo.DomHelper.append(document.body,
53150                         config.autoCreate, true);
53151         }else{
53152             this.el = Roo.DomHelper.append(document.body,
53153                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
53154         }
53155     }
53156     this.closable = false;
53157     this.loaded = false;
53158     this.active = false;
53159     if(typeof config == "string"){
53160         this.title = config;
53161     }else{
53162         Roo.apply(this, config);
53163     }
53164     
53165     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
53166         this.wrapEl = this.el.wrap();
53167         this.toolbar.container = this.el.insertSibling(false, 'before');
53168         this.toolbar = new Roo.Toolbar(this.toolbar);
53169     }
53170     
53171     // xtype created footer. - not sure if will work as we normally have to render first..
53172     if (this.footer && !this.footer.el && this.footer.xtype) {
53173         if (!this.wrapEl) {
53174             this.wrapEl = this.el.wrap();
53175         }
53176     
53177         this.footer.container = this.wrapEl.createChild();
53178          
53179         this.footer = Roo.factory(this.footer, Roo);
53180         
53181     }
53182     
53183     if(this.resizeEl){
53184         this.resizeEl = Roo.get(this.resizeEl, true);
53185     }else{
53186         this.resizeEl = this.el;
53187     }
53188     // handle view.xtype
53189     
53190  
53191     
53192     
53193     this.addEvents({
53194         /**
53195          * @event activate
53196          * Fires when this panel is activated. 
53197          * @param {Roo.ContentPanel} this
53198          */
53199         "activate" : true,
53200         /**
53201          * @event deactivate
53202          * Fires when this panel is activated. 
53203          * @param {Roo.ContentPanel} this
53204          */
53205         "deactivate" : true,
53206
53207         /**
53208          * @event resize
53209          * Fires when this panel is resized if fitToFrame is true.
53210          * @param {Roo.ContentPanel} this
53211          * @param {Number} width The width after any component adjustments
53212          * @param {Number} height The height after any component adjustments
53213          */
53214         "resize" : true,
53215         
53216          /**
53217          * @event render
53218          * Fires when this tab is created
53219          * @param {Roo.ContentPanel} this
53220          */
53221         "render" : true
53222         
53223         
53224         
53225     });
53226     
53227
53228     
53229     
53230     if(this.autoScroll){
53231         this.resizeEl.setStyle("overflow", "auto");
53232     } else {
53233         // fix randome scrolling
53234         this.el.on('scroll', function() {
53235             Roo.log('fix random scolling');
53236             this.scrollTo('top',0); 
53237         });
53238     }
53239     content = content || this.content;
53240     if(content){
53241         this.setContent(content);
53242     }
53243     if(config && config.url){
53244         this.setUrl(this.url, this.params, this.loadOnce);
53245     }
53246     
53247     
53248     
53249     Roo.ContentPanel.superclass.constructor.call(this);
53250     
53251     if (this.view && typeof(this.view.xtype) != 'undefined') {
53252         this.view.el = this.el.appendChild(document.createElement("div"));
53253         this.view = Roo.factory(this.view); 
53254         this.view.render  &&  this.view.render(false, '');  
53255     }
53256     
53257     
53258     this.fireEvent('render', this);
53259 };
53260
53261 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
53262     tabTip:'',
53263     setRegion : function(region){
53264         this.region = region;
53265         if(region){
53266            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
53267         }else{
53268            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
53269         } 
53270     },
53271     
53272     /**
53273      * Returns the toolbar for this Panel if one was configured. 
53274      * @return {Roo.Toolbar} 
53275      */
53276     getToolbar : function(){
53277         return this.toolbar;
53278     },
53279     
53280     setActiveState : function(active){
53281         this.active = active;
53282         if(!active){
53283             this.fireEvent("deactivate", this);
53284         }else{
53285             this.fireEvent("activate", this);
53286         }
53287     },
53288     /**
53289      * Updates this panel's element
53290      * @param {String} content The new content
53291      * @param {Boolean} loadScripts (optional) true to look for and process scripts
53292     */
53293     setContent : function(content, loadScripts){
53294         this.el.update(content, loadScripts);
53295     },
53296
53297     ignoreResize : function(w, h){
53298         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
53299             return true;
53300         }else{
53301             this.lastSize = {width: w, height: h};
53302             return false;
53303         }
53304     },
53305     /**
53306      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
53307      * @return {Roo.UpdateManager} The UpdateManager
53308      */
53309     getUpdateManager : function(){
53310         return this.el.getUpdateManager();
53311     },
53312      /**
53313      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
53314      * @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:
53315 <pre><code>
53316 panel.load({
53317     url: "your-url.php",
53318     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
53319     callback: yourFunction,
53320     scope: yourObject, //(optional scope)
53321     discardUrl: false,
53322     nocache: false,
53323     text: "Loading...",
53324     timeout: 30,
53325     scripts: false
53326 });
53327 </code></pre>
53328      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
53329      * 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.
53330      * @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}
53331      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
53332      * @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.
53333      * @return {Roo.ContentPanel} this
53334      */
53335     load : function(){
53336         var um = this.el.getUpdateManager();
53337         um.update.apply(um, arguments);
53338         return this;
53339     },
53340
53341
53342     /**
53343      * 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.
53344      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
53345      * @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)
53346      * @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)
53347      * @return {Roo.UpdateManager} The UpdateManager
53348      */
53349     setUrl : function(url, params, loadOnce){
53350         if(this.refreshDelegate){
53351             this.removeListener("activate", this.refreshDelegate);
53352         }
53353         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
53354         this.on("activate", this.refreshDelegate);
53355         return this.el.getUpdateManager();
53356     },
53357     
53358     _handleRefresh : function(url, params, loadOnce){
53359         if(!loadOnce || !this.loaded){
53360             var updater = this.el.getUpdateManager();
53361             updater.update(url, params, this._setLoaded.createDelegate(this));
53362         }
53363     },
53364     
53365     _setLoaded : function(){
53366         this.loaded = true;
53367     }, 
53368     
53369     /**
53370      * Returns this panel's id
53371      * @return {String} 
53372      */
53373     getId : function(){
53374         return this.el.id;
53375     },
53376     
53377     /** 
53378      * Returns this panel's element - used by regiosn to add.
53379      * @return {Roo.Element} 
53380      */
53381     getEl : function(){
53382         return this.wrapEl || this.el;
53383     },
53384     
53385     adjustForComponents : function(width, height)
53386     {
53387         //Roo.log('adjustForComponents ');
53388         if(this.resizeEl != this.el){
53389             width -= this.el.getFrameWidth('lr');
53390             height -= this.el.getFrameWidth('tb');
53391         }
53392         if(this.toolbar){
53393             var te = this.toolbar.getEl();
53394             height -= te.getHeight();
53395             te.setWidth(width);
53396         }
53397         if(this.footer){
53398             var te = this.footer.getEl();
53399             Roo.log("footer:" + te.getHeight());
53400             
53401             height -= te.getHeight();
53402             te.setWidth(width);
53403         }
53404         
53405         
53406         if(this.adjustments){
53407             width += this.adjustments[0];
53408             height += this.adjustments[1];
53409         }
53410         return {"width": width, "height": height};
53411     },
53412     
53413     setSize : function(width, height){
53414         if(this.fitToFrame && !this.ignoreResize(width, height)){
53415             if(this.fitContainer && this.resizeEl != this.el){
53416                 this.el.setSize(width, height);
53417             }
53418             var size = this.adjustForComponents(width, height);
53419             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
53420             this.fireEvent('resize', this, size.width, size.height);
53421         }
53422     },
53423     
53424     /**
53425      * Returns this panel's title
53426      * @return {String} 
53427      */
53428     getTitle : function(){
53429         return this.title;
53430     },
53431     
53432     /**
53433      * Set this panel's title
53434      * @param {String} title
53435      */
53436     setTitle : function(title){
53437         this.title = title;
53438         if(this.region){
53439             this.region.updatePanelTitle(this, title);
53440         }
53441     },
53442     
53443     /**
53444      * Returns true is this panel was configured to be closable
53445      * @return {Boolean} 
53446      */
53447     isClosable : function(){
53448         return this.closable;
53449     },
53450     
53451     beforeSlide : function(){
53452         this.el.clip();
53453         this.resizeEl.clip();
53454     },
53455     
53456     afterSlide : function(){
53457         this.el.unclip();
53458         this.resizeEl.unclip();
53459     },
53460     
53461     /**
53462      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
53463      *   Will fail silently if the {@link #setUrl} method has not been called.
53464      *   This does not activate the panel, just updates its content.
53465      */
53466     refresh : function(){
53467         if(this.refreshDelegate){
53468            this.loaded = false;
53469            this.refreshDelegate();
53470         }
53471     },
53472     
53473     /**
53474      * Destroys this panel
53475      */
53476     destroy : function(){
53477         this.el.removeAllListeners();
53478         var tempEl = document.createElement("span");
53479         tempEl.appendChild(this.el.dom);
53480         tempEl.innerHTML = "";
53481         this.el.remove();
53482         this.el = null;
53483     },
53484     
53485     /**
53486      * form - if the content panel contains a form - this is a reference to it.
53487      * @type {Roo.form.Form}
53488      */
53489     form : false,
53490     /**
53491      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
53492      *    This contains a reference to it.
53493      * @type {Roo.View}
53494      */
53495     view : false,
53496     
53497       /**
53498      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
53499      * <pre><code>
53500
53501 layout.addxtype({
53502        xtype : 'Form',
53503        items: [ .... ]
53504    }
53505 );
53506
53507 </code></pre>
53508      * @param {Object} cfg Xtype definition of item to add.
53509      */
53510     
53511     addxtype : function(cfg) {
53512         // add form..
53513         if (cfg.xtype.match(/^Form$/)) {
53514             
53515             var el;
53516             //if (this.footer) {
53517             //    el = this.footer.container.insertSibling(false, 'before');
53518             //} else {
53519                 el = this.el.createChild();
53520             //}
53521
53522             this.form = new  Roo.form.Form(cfg);
53523             
53524             
53525             if ( this.form.allItems.length) {
53526                 this.form.render(el.dom);
53527             }
53528             return this.form;
53529         }
53530         // should only have one of theses..
53531         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
53532             // views.. should not be just added - used named prop 'view''
53533             
53534             cfg.el = this.el.appendChild(document.createElement("div"));
53535             // factory?
53536             
53537             var ret = new Roo.factory(cfg);
53538              
53539              ret.render && ret.render(false, ''); // render blank..
53540             this.view = ret;
53541             return ret;
53542         }
53543         return false;
53544     }
53545 });
53546
53547 /**
53548  * @class Roo.GridPanel
53549  * @extends Roo.ContentPanel
53550  * @constructor
53551  * Create a new GridPanel.
53552  * @param {Roo.grid.Grid} grid The grid for this panel
53553  * @param {String/Object} config A string to set only the panel's title, or a config object
53554  */
53555 Roo.GridPanel = function(grid, config){
53556     
53557   
53558     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
53559         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
53560         
53561     this.wrapper.dom.appendChild(grid.getGridEl().dom);
53562     
53563     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
53564     
53565     if(this.toolbar){
53566         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
53567     }
53568     // xtype created footer. - not sure if will work as we normally have to render first..
53569     if (this.footer && !this.footer.el && this.footer.xtype) {
53570         
53571         this.footer.container = this.grid.getView().getFooterPanel(true);
53572         this.footer.dataSource = this.grid.dataSource;
53573         this.footer = Roo.factory(this.footer, Roo);
53574         
53575     }
53576     
53577     grid.monitorWindowResize = false; // turn off autosizing
53578     grid.autoHeight = false;
53579     grid.autoWidth = false;
53580     this.grid = grid;
53581     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
53582 };
53583
53584 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
53585     getId : function(){
53586         return this.grid.id;
53587     },
53588     
53589     /**
53590      * Returns the grid for this panel
53591      * @return {Roo.grid.Grid} 
53592      */
53593     getGrid : function(){
53594         return this.grid;    
53595     },
53596     
53597     setSize : function(width, height){
53598         if(!this.ignoreResize(width, height)){
53599             var grid = this.grid;
53600             var size = this.adjustForComponents(width, height);
53601             grid.getGridEl().setSize(size.width, size.height);
53602             grid.autoSize();
53603         }
53604     },
53605     
53606     beforeSlide : function(){
53607         this.grid.getView().scroller.clip();
53608     },
53609     
53610     afterSlide : function(){
53611         this.grid.getView().scroller.unclip();
53612     },
53613     
53614     destroy : function(){
53615         this.grid.destroy();
53616         delete this.grid;
53617         Roo.GridPanel.superclass.destroy.call(this); 
53618     }
53619 });
53620
53621
53622 /**
53623  * @class Roo.NestedLayoutPanel
53624  * @extends Roo.ContentPanel
53625  * @constructor
53626  * Create a new NestedLayoutPanel.
53627  * 
53628  * 
53629  * @param {Roo.BorderLayout} layout The layout for this panel
53630  * @param {String/Object} config A string to set only the title or a config object
53631  */
53632 Roo.NestedLayoutPanel = function(layout, config)
53633 {
53634     // construct with only one argument..
53635     /* FIXME - implement nicer consturctors
53636     if (layout.layout) {
53637         config = layout;
53638         layout = config.layout;
53639         delete config.layout;
53640     }
53641     if (layout.xtype && !layout.getEl) {
53642         // then layout needs constructing..
53643         layout = Roo.factory(layout, Roo);
53644     }
53645     */
53646     
53647     
53648     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
53649     
53650     layout.monitorWindowResize = false; // turn off autosizing
53651     this.layout = layout;
53652     this.layout.getEl().addClass("x-layout-nested-layout");
53653     
53654     
53655     
53656     
53657 };
53658
53659 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
53660
53661     setSize : function(width, height){
53662         if(!this.ignoreResize(width, height)){
53663             var size = this.adjustForComponents(width, height);
53664             var el = this.layout.getEl();
53665             el.setSize(size.width, size.height);
53666             var touch = el.dom.offsetWidth;
53667             this.layout.layout();
53668             // ie requires a double layout on the first pass
53669             if(Roo.isIE && !this.initialized){
53670                 this.initialized = true;
53671                 this.layout.layout();
53672             }
53673         }
53674     },
53675     
53676     // activate all subpanels if not currently active..
53677     
53678     setActiveState : function(active){
53679         this.active = active;
53680         if(!active){
53681             this.fireEvent("deactivate", this);
53682             return;
53683         }
53684         
53685         this.fireEvent("activate", this);
53686         // not sure if this should happen before or after..
53687         if (!this.layout) {
53688             return; // should not happen..
53689         }
53690         var reg = false;
53691         for (var r in this.layout.regions) {
53692             reg = this.layout.getRegion(r);
53693             if (reg.getActivePanel()) {
53694                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
53695                 reg.setActivePanel(reg.getActivePanel());
53696                 continue;
53697             }
53698             if (!reg.panels.length) {
53699                 continue;
53700             }
53701             reg.showPanel(reg.getPanel(0));
53702         }
53703         
53704         
53705         
53706         
53707     },
53708     
53709     /**
53710      * Returns the nested BorderLayout for this panel
53711      * @return {Roo.BorderLayout} 
53712      */
53713     getLayout : function(){
53714         return this.layout;
53715     },
53716     
53717      /**
53718      * Adds a xtype elements to the layout of the nested panel
53719      * <pre><code>
53720
53721 panel.addxtype({
53722        xtype : 'ContentPanel',
53723        region: 'west',
53724        items: [ .... ]
53725    }
53726 );
53727
53728 panel.addxtype({
53729         xtype : 'NestedLayoutPanel',
53730         region: 'west',
53731         layout: {
53732            center: { },
53733            west: { }   
53734         },
53735         items : [ ... list of content panels or nested layout panels.. ]
53736    }
53737 );
53738 </code></pre>
53739      * @param {Object} cfg Xtype definition of item to add.
53740      */
53741     addxtype : function(cfg) {
53742         return this.layout.addxtype(cfg);
53743     
53744     }
53745 });
53746
53747 Roo.ScrollPanel = function(el, config, content){
53748     config = config || {};
53749     config.fitToFrame = true;
53750     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
53751     
53752     this.el.dom.style.overflow = "hidden";
53753     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
53754     this.el.removeClass("x-layout-inactive-content");
53755     this.el.on("mousewheel", this.onWheel, this);
53756
53757     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
53758     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
53759     up.unselectable(); down.unselectable();
53760     up.on("click", this.scrollUp, this);
53761     down.on("click", this.scrollDown, this);
53762     up.addClassOnOver("x-scroller-btn-over");
53763     down.addClassOnOver("x-scroller-btn-over");
53764     up.addClassOnClick("x-scroller-btn-click");
53765     down.addClassOnClick("x-scroller-btn-click");
53766     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
53767
53768     this.resizeEl = this.el;
53769     this.el = wrap; this.up = up; this.down = down;
53770 };
53771
53772 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
53773     increment : 100,
53774     wheelIncrement : 5,
53775     scrollUp : function(){
53776         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
53777     },
53778
53779     scrollDown : function(){
53780         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
53781     },
53782
53783     afterScroll : function(){
53784         var el = this.resizeEl;
53785         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
53786         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53787         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53788     },
53789
53790     setSize : function(){
53791         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
53792         this.afterScroll();
53793     },
53794
53795     onWheel : function(e){
53796         var d = e.getWheelDelta();
53797         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
53798         this.afterScroll();
53799         e.stopEvent();
53800     },
53801
53802     setContent : function(content, loadScripts){
53803         this.resizeEl.update(content, loadScripts);
53804     }
53805
53806 });
53807
53808
53809
53810
53811
53812
53813
53814
53815
53816 /**
53817  * @class Roo.TreePanel
53818  * @extends Roo.ContentPanel
53819  * @constructor
53820  * Create a new TreePanel. - defaults to fit/scoll contents.
53821  * @param {String/Object} config A string to set only the panel's title, or a config object
53822  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
53823  */
53824 Roo.TreePanel = function(config){
53825     var el = config.el;
53826     var tree = config.tree;
53827     delete config.tree; 
53828     delete config.el; // hopefull!
53829     
53830     // wrapper for IE7 strict & safari scroll issue
53831     
53832     var treeEl = el.createChild();
53833     config.resizeEl = treeEl;
53834     
53835     
53836     
53837     Roo.TreePanel.superclass.constructor.call(this, el, config);
53838  
53839  
53840     this.tree = new Roo.tree.TreePanel(treeEl , tree);
53841     //console.log(tree);
53842     this.on('activate', function()
53843     {
53844         if (this.tree.rendered) {
53845             return;
53846         }
53847         //console.log('render tree');
53848         this.tree.render();
53849     });
53850     // this should not be needed.. - it's actually the 'el' that resizes?
53851     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
53852     
53853     //this.on('resize',  function (cp, w, h) {
53854     //        this.tree.innerCt.setWidth(w);
53855     //        this.tree.innerCt.setHeight(h);
53856     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
53857     //});
53858
53859         
53860     
53861 };
53862
53863 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
53864     fitToFrame : true,
53865     autoScroll : true
53866 });
53867
53868
53869
53870
53871
53872
53873
53874
53875
53876
53877
53878 /*
53879  * Based on:
53880  * Ext JS Library 1.1.1
53881  * Copyright(c) 2006-2007, Ext JS, LLC.
53882  *
53883  * Originally Released Under LGPL - original licence link has changed is not relivant.
53884  *
53885  * Fork - LGPL
53886  * <script type="text/javascript">
53887  */
53888  
53889
53890 /**
53891  * @class Roo.ReaderLayout
53892  * @extends Roo.BorderLayout
53893  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
53894  * center region containing two nested regions (a top one for a list view and one for item preview below),
53895  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
53896  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
53897  * expedites the setup of the overall layout and regions for this common application style.
53898  * Example:
53899  <pre><code>
53900 var reader = new Roo.ReaderLayout();
53901 var CP = Roo.ContentPanel;  // shortcut for adding
53902
53903 reader.beginUpdate();
53904 reader.add("north", new CP("north", "North"));
53905 reader.add("west", new CP("west", {title: "West"}));
53906 reader.add("east", new CP("east", {title: "East"}));
53907
53908 reader.regions.listView.add(new CP("listView", "List"));
53909 reader.regions.preview.add(new CP("preview", "Preview"));
53910 reader.endUpdate();
53911 </code></pre>
53912 * @constructor
53913 * Create a new ReaderLayout
53914 * @param {Object} config Configuration options
53915 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
53916 * document.body if omitted)
53917 */
53918 Roo.ReaderLayout = function(config, renderTo){
53919     var c = config || {size:{}};
53920     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
53921         north: c.north !== false ? Roo.apply({
53922             split:false,
53923             initialSize: 32,
53924             titlebar: false
53925         }, c.north) : false,
53926         west: c.west !== false ? Roo.apply({
53927             split:true,
53928             initialSize: 200,
53929             minSize: 175,
53930             maxSize: 400,
53931             titlebar: true,
53932             collapsible: true,
53933             animate: true,
53934             margins:{left:5,right:0,bottom:5,top:5},
53935             cmargins:{left:5,right:5,bottom:5,top:5}
53936         }, c.west) : false,
53937         east: c.east !== false ? Roo.apply({
53938             split:true,
53939             initialSize: 200,
53940             minSize: 175,
53941             maxSize: 400,
53942             titlebar: true,
53943             collapsible: true,
53944             animate: true,
53945             margins:{left:0,right:5,bottom:5,top:5},
53946             cmargins:{left:5,right:5,bottom:5,top:5}
53947         }, c.east) : false,
53948         center: Roo.apply({
53949             tabPosition: 'top',
53950             autoScroll:false,
53951             closeOnTab: true,
53952             titlebar:false,
53953             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
53954         }, c.center)
53955     });
53956
53957     this.el.addClass('x-reader');
53958
53959     this.beginUpdate();
53960
53961     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
53962         south: c.preview !== false ? Roo.apply({
53963             split:true,
53964             initialSize: 200,
53965             minSize: 100,
53966             autoScroll:true,
53967             collapsible:true,
53968             titlebar: true,
53969             cmargins:{top:5,left:0, right:0, bottom:0}
53970         }, c.preview) : false,
53971         center: Roo.apply({
53972             autoScroll:false,
53973             titlebar:false,
53974             minHeight:200
53975         }, c.listView)
53976     });
53977     this.add('center', new Roo.NestedLayoutPanel(inner,
53978             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
53979
53980     this.endUpdate();
53981
53982     this.regions.preview = inner.getRegion('south');
53983     this.regions.listView = inner.getRegion('center');
53984 };
53985
53986 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
53987  * Based on:
53988  * Ext JS Library 1.1.1
53989  * Copyright(c) 2006-2007, Ext JS, LLC.
53990  *
53991  * Originally Released Under LGPL - original licence link has changed is not relivant.
53992  *
53993  * Fork - LGPL
53994  * <script type="text/javascript">
53995  */
53996  
53997 /**
53998  * @class Roo.grid.Grid
53999  * @extends Roo.util.Observable
54000  * This class represents the primary interface of a component based grid control.
54001  * <br><br>Usage:<pre><code>
54002  var grid = new Roo.grid.Grid("my-container-id", {
54003      ds: myDataStore,
54004      cm: myColModel,
54005      selModel: mySelectionModel,
54006      autoSizeColumns: true,
54007      monitorWindowResize: false,
54008      trackMouseOver: true
54009  });
54010  // set any options
54011  grid.render();
54012  * </code></pre>
54013  * <b>Common Problems:</b><br/>
54014  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
54015  * element will correct this<br/>
54016  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
54017  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
54018  * are unpredictable.<br/>
54019  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
54020  * grid to calculate dimensions/offsets.<br/>
54021   * @constructor
54022  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
54023  * The container MUST have some type of size defined for the grid to fill. The container will be
54024  * automatically set to position relative if it isn't already.
54025  * @param {Object} config A config object that sets properties on this grid.
54026  */
54027 Roo.grid.Grid = function(container, config){
54028         // initialize the container
54029         this.container = Roo.get(container);
54030         this.container.update("");
54031         this.container.setStyle("overflow", "hidden");
54032     this.container.addClass('x-grid-container');
54033
54034     this.id = this.container.id;
54035
54036     Roo.apply(this, config);
54037     // check and correct shorthanded configs
54038     if(this.ds){
54039         this.dataSource = this.ds;
54040         delete this.ds;
54041     }
54042     if(this.cm){
54043         this.colModel = this.cm;
54044         delete this.cm;
54045     }
54046     if(this.sm){
54047         this.selModel = this.sm;
54048         delete this.sm;
54049     }
54050
54051     if (this.selModel) {
54052         this.selModel = Roo.factory(this.selModel, Roo.grid);
54053         this.sm = this.selModel;
54054         this.sm.xmodule = this.xmodule || false;
54055     }
54056     if (typeof(this.colModel.config) == 'undefined') {
54057         this.colModel = new Roo.grid.ColumnModel(this.colModel);
54058         this.cm = this.colModel;
54059         this.cm.xmodule = this.xmodule || false;
54060     }
54061     if (this.dataSource) {
54062         this.dataSource= Roo.factory(this.dataSource, Roo.data);
54063         this.ds = this.dataSource;
54064         this.ds.xmodule = this.xmodule || false;
54065          
54066     }
54067     
54068     
54069     
54070     if(this.width){
54071         this.container.setWidth(this.width);
54072     }
54073
54074     if(this.height){
54075         this.container.setHeight(this.height);
54076     }
54077     /** @private */
54078         this.addEvents({
54079         // raw events
54080         /**
54081          * @event click
54082          * The raw click event for the entire grid.
54083          * @param {Roo.EventObject} e
54084          */
54085         "click" : true,
54086         /**
54087          * @event dblclick
54088          * The raw dblclick event for the entire grid.
54089          * @param {Roo.EventObject} e
54090          */
54091         "dblclick" : true,
54092         /**
54093          * @event contextmenu
54094          * The raw contextmenu event for the entire grid.
54095          * @param {Roo.EventObject} e
54096          */
54097         "contextmenu" : true,
54098         /**
54099          * @event mousedown
54100          * The raw mousedown event for the entire grid.
54101          * @param {Roo.EventObject} e
54102          */
54103         "mousedown" : true,
54104         /**
54105          * @event mouseup
54106          * The raw mouseup event for the entire grid.
54107          * @param {Roo.EventObject} e
54108          */
54109         "mouseup" : true,
54110         /**
54111          * @event mouseover
54112          * The raw mouseover event for the entire grid.
54113          * @param {Roo.EventObject} e
54114          */
54115         "mouseover" : true,
54116         /**
54117          * @event mouseout
54118          * The raw mouseout event for the entire grid.
54119          * @param {Roo.EventObject} e
54120          */
54121         "mouseout" : true,
54122         /**
54123          * @event keypress
54124          * The raw keypress event for the entire grid.
54125          * @param {Roo.EventObject} e
54126          */
54127         "keypress" : true,
54128         /**
54129          * @event keydown
54130          * The raw keydown event for the entire grid.
54131          * @param {Roo.EventObject} e
54132          */
54133         "keydown" : true,
54134
54135         // custom events
54136
54137         /**
54138          * @event cellclick
54139          * Fires when a cell is clicked
54140          * @param {Grid} this
54141          * @param {Number} rowIndex
54142          * @param {Number} columnIndex
54143          * @param {Roo.EventObject} e
54144          */
54145         "cellclick" : true,
54146         /**
54147          * @event celldblclick
54148          * Fires when a cell is double clicked
54149          * @param {Grid} this
54150          * @param {Number} rowIndex
54151          * @param {Number} columnIndex
54152          * @param {Roo.EventObject} e
54153          */
54154         "celldblclick" : true,
54155         /**
54156          * @event rowclick
54157          * Fires when a row is clicked
54158          * @param {Grid} this
54159          * @param {Number} rowIndex
54160          * @param {Roo.EventObject} e
54161          */
54162         "rowclick" : true,
54163         /**
54164          * @event rowdblclick
54165          * Fires when a row is double clicked
54166          * @param {Grid} this
54167          * @param {Number} rowIndex
54168          * @param {Roo.EventObject} e
54169          */
54170         "rowdblclick" : true,
54171         /**
54172          * @event headerclick
54173          * Fires when a header is clicked
54174          * @param {Grid} this
54175          * @param {Number} columnIndex
54176          * @param {Roo.EventObject} e
54177          */
54178         "headerclick" : true,
54179         /**
54180          * @event headerdblclick
54181          * Fires when a header cell is double clicked
54182          * @param {Grid} this
54183          * @param {Number} columnIndex
54184          * @param {Roo.EventObject} e
54185          */
54186         "headerdblclick" : true,
54187         /**
54188          * @event rowcontextmenu
54189          * Fires when a row is right clicked
54190          * @param {Grid} this
54191          * @param {Number} rowIndex
54192          * @param {Roo.EventObject} e
54193          */
54194         "rowcontextmenu" : true,
54195         /**
54196          * @event cellcontextmenu
54197          * Fires when a cell is right clicked
54198          * @param {Grid} this
54199          * @param {Number} rowIndex
54200          * @param {Number} cellIndex
54201          * @param {Roo.EventObject} e
54202          */
54203          "cellcontextmenu" : true,
54204         /**
54205          * @event headercontextmenu
54206          * Fires when a header is right clicked
54207          * @param {Grid} this
54208          * @param {Number} columnIndex
54209          * @param {Roo.EventObject} e
54210          */
54211         "headercontextmenu" : true,
54212         /**
54213          * @event bodyscroll
54214          * Fires when the body element is scrolled
54215          * @param {Number} scrollLeft
54216          * @param {Number} scrollTop
54217          */
54218         "bodyscroll" : true,
54219         /**
54220          * @event columnresize
54221          * Fires when the user resizes a column
54222          * @param {Number} columnIndex
54223          * @param {Number} newSize
54224          */
54225         "columnresize" : true,
54226         /**
54227          * @event columnmove
54228          * Fires when the user moves a column
54229          * @param {Number} oldIndex
54230          * @param {Number} newIndex
54231          */
54232         "columnmove" : true,
54233         /**
54234          * @event startdrag
54235          * Fires when row(s) start being dragged
54236          * @param {Grid} this
54237          * @param {Roo.GridDD} dd The drag drop object
54238          * @param {event} e The raw browser event
54239          */
54240         "startdrag" : true,
54241         /**
54242          * @event enddrag
54243          * Fires when a drag operation is complete
54244          * @param {Grid} this
54245          * @param {Roo.GridDD} dd The drag drop object
54246          * @param {event} e The raw browser event
54247          */
54248         "enddrag" : true,
54249         /**
54250          * @event dragdrop
54251          * Fires when dragged row(s) are dropped on a valid DD target
54252          * @param {Grid} this
54253          * @param {Roo.GridDD} dd The drag drop object
54254          * @param {String} targetId The target drag drop object
54255          * @param {event} e The raw browser event
54256          */
54257         "dragdrop" : true,
54258         /**
54259          * @event dragover
54260          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
54261          * @param {Grid} this
54262          * @param {Roo.GridDD} dd The drag drop object
54263          * @param {String} targetId The target drag drop object
54264          * @param {event} e The raw browser event
54265          */
54266         "dragover" : true,
54267         /**
54268          * @event dragenter
54269          *  Fires when the dragged row(s) first cross another DD target while being dragged
54270          * @param {Grid} this
54271          * @param {Roo.GridDD} dd The drag drop object
54272          * @param {String} targetId The target drag drop object
54273          * @param {event} e The raw browser event
54274          */
54275         "dragenter" : true,
54276         /**
54277          * @event dragout
54278          * Fires when the dragged row(s) leave another DD target while being dragged
54279          * @param {Grid} this
54280          * @param {Roo.GridDD} dd The drag drop object
54281          * @param {String} targetId The target drag drop object
54282          * @param {event} e The raw browser event
54283          */
54284         "dragout" : true,
54285         /**
54286          * @event rowclass
54287          * Fires when a row is rendered, so you can change add a style to it.
54288          * @param {GridView} gridview   The grid view
54289          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
54290          */
54291         'rowclass' : true,
54292
54293         /**
54294          * @event render
54295          * Fires when the grid is rendered
54296          * @param {Grid} grid
54297          */
54298         'render' : true
54299     });
54300
54301     Roo.grid.Grid.superclass.constructor.call(this);
54302 };
54303 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
54304     
54305     /**
54306      * @cfg {String} ddGroup - drag drop group.
54307      */
54308
54309     /**
54310      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
54311      */
54312     minColumnWidth : 25,
54313
54314     /**
54315      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
54316      * <b>on initial render.</b> It is more efficient to explicitly size the columns
54317      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
54318      */
54319     autoSizeColumns : false,
54320
54321     /**
54322      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
54323      */
54324     autoSizeHeaders : true,
54325
54326     /**
54327      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
54328      */
54329     monitorWindowResize : true,
54330
54331     /**
54332      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
54333      * rows measured to get a columns size. Default is 0 (all rows).
54334      */
54335     maxRowsToMeasure : 0,
54336
54337     /**
54338      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
54339      */
54340     trackMouseOver : true,
54341
54342     /**
54343     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
54344     */
54345     
54346     /**
54347     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
54348     */
54349     enableDragDrop : false,
54350     
54351     /**
54352     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
54353     */
54354     enableColumnMove : true,
54355     
54356     /**
54357     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
54358     */
54359     enableColumnHide : true,
54360     
54361     /**
54362     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
54363     */
54364     enableRowHeightSync : false,
54365     
54366     /**
54367     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
54368     */
54369     stripeRows : true,
54370     
54371     /**
54372     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
54373     */
54374     autoHeight : false,
54375
54376     /**
54377      * @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.
54378      */
54379     autoExpandColumn : false,
54380
54381     /**
54382     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
54383     * Default is 50.
54384     */
54385     autoExpandMin : 50,
54386
54387     /**
54388     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
54389     */
54390     autoExpandMax : 1000,
54391
54392     /**
54393     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
54394     */
54395     view : null,
54396
54397     /**
54398     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
54399     */
54400     loadMask : false,
54401     /**
54402     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
54403     */
54404     dropTarget: false,
54405     
54406    
54407     
54408     // private
54409     rendered : false,
54410
54411     /**
54412     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
54413     * of a fixed width. Default is false.
54414     */
54415     /**
54416     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
54417     */
54418     /**
54419      * Called once after all setup has been completed and the grid is ready to be rendered.
54420      * @return {Roo.grid.Grid} this
54421      */
54422     render : function()
54423     {
54424         var c = this.container;
54425         // try to detect autoHeight/width mode
54426         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
54427             this.autoHeight = true;
54428         }
54429         var view = this.getView();
54430         view.init(this);
54431
54432         c.on("click", this.onClick, this);
54433         c.on("dblclick", this.onDblClick, this);
54434         c.on("contextmenu", this.onContextMenu, this);
54435         c.on("keydown", this.onKeyDown, this);
54436         if (Roo.isTouch) {
54437             c.on("touchstart", this.onTouchStart, this);
54438         }
54439
54440         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
54441
54442         this.getSelectionModel().init(this);
54443
54444         view.render();
54445
54446         if(this.loadMask){
54447             this.loadMask = new Roo.LoadMask(this.container,
54448                     Roo.apply({store:this.dataSource}, this.loadMask));
54449         }
54450         
54451         
54452         if (this.toolbar && this.toolbar.xtype) {
54453             this.toolbar.container = this.getView().getHeaderPanel(true);
54454             this.toolbar = new Roo.Toolbar(this.toolbar);
54455         }
54456         if (this.footer && this.footer.xtype) {
54457             this.footer.dataSource = this.getDataSource();
54458             this.footer.container = this.getView().getFooterPanel(true);
54459             this.footer = Roo.factory(this.footer, Roo);
54460         }
54461         if (this.dropTarget && this.dropTarget.xtype) {
54462             delete this.dropTarget.xtype;
54463             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
54464         }
54465         
54466         
54467         this.rendered = true;
54468         this.fireEvent('render', this);
54469         return this;
54470     },
54471
54472         /**
54473          * Reconfigures the grid to use a different Store and Column Model.
54474          * The View will be bound to the new objects and refreshed.
54475          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
54476          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
54477          */
54478     reconfigure : function(dataSource, colModel){
54479         if(this.loadMask){
54480             this.loadMask.destroy();
54481             this.loadMask = new Roo.LoadMask(this.container,
54482                     Roo.apply({store:dataSource}, this.loadMask));
54483         }
54484         this.view.bind(dataSource, colModel);
54485         this.dataSource = dataSource;
54486         this.colModel = colModel;
54487         this.view.refresh(true);
54488     },
54489
54490     // private
54491     onKeyDown : function(e){
54492         this.fireEvent("keydown", e);
54493     },
54494
54495     /**
54496      * Destroy this grid.
54497      * @param {Boolean} removeEl True to remove the element
54498      */
54499     destroy : function(removeEl, keepListeners){
54500         if(this.loadMask){
54501             this.loadMask.destroy();
54502         }
54503         var c = this.container;
54504         c.removeAllListeners();
54505         this.view.destroy();
54506         this.colModel.purgeListeners();
54507         if(!keepListeners){
54508             this.purgeListeners();
54509         }
54510         c.update("");
54511         if(removeEl === true){
54512             c.remove();
54513         }
54514     },
54515
54516     // private
54517     processEvent : function(name, e){
54518         // does this fire select???
54519         //Roo.log('grid:processEvent '  + name);
54520         
54521         if (name != 'touchstart' ) {
54522             this.fireEvent(name, e);    
54523         }
54524         
54525         var t = e.getTarget();
54526         var v = this.view;
54527         var header = v.findHeaderIndex(t);
54528         if(header !== false){
54529             var ename = name == 'touchstart' ? 'click' : name;
54530              
54531             this.fireEvent("header" + ename, this, header, e);
54532         }else{
54533             var row = v.findRowIndex(t);
54534             var cell = v.findCellIndex(t);
54535             if (name == 'touchstart') {
54536                 // first touch is always a click.
54537                 // hopefull this happens after selection is updated.?
54538                 name = false;
54539                 
54540                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
54541                     var cs = this.selModel.getSelectedCell();
54542                     if (row == cs[0] && cell == cs[1]){
54543                         name = 'dblclick';
54544                     }
54545                 }
54546                 if (typeof(this.selModel.getSelections) != 'undefined') {
54547                     var cs = this.selModel.getSelections();
54548                     var ds = this.dataSource;
54549                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
54550                         name = 'dblclick';
54551                     }
54552                 }
54553                 if (!name) {
54554                     return;
54555                 }
54556             }
54557             
54558             
54559             if(row !== false){
54560                 this.fireEvent("row" + name, this, row, e);
54561                 if(cell !== false){
54562                     this.fireEvent("cell" + name, this, row, cell, e);
54563                 }
54564             }
54565         }
54566     },
54567
54568     // private
54569     onClick : function(e){
54570         this.processEvent("click", e);
54571     },
54572    // private
54573     onTouchStart : function(e){
54574         this.processEvent("touchstart", e);
54575     },
54576
54577     // private
54578     onContextMenu : function(e, t){
54579         this.processEvent("contextmenu", e);
54580     },
54581
54582     // private
54583     onDblClick : function(e){
54584         this.processEvent("dblclick", e);
54585     },
54586
54587     // private
54588     walkCells : function(row, col, step, fn, scope){
54589         var cm = this.colModel, clen = cm.getColumnCount();
54590         var ds = this.dataSource, rlen = ds.getCount(), first = true;
54591         if(step < 0){
54592             if(col < 0){
54593                 row--;
54594                 first = false;
54595             }
54596             while(row >= 0){
54597                 if(!first){
54598                     col = clen-1;
54599                 }
54600                 first = false;
54601                 while(col >= 0){
54602                     if(fn.call(scope || this, row, col, cm) === true){
54603                         return [row, col];
54604                     }
54605                     col--;
54606                 }
54607                 row--;
54608             }
54609         } else {
54610             if(col >= clen){
54611                 row++;
54612                 first = false;
54613             }
54614             while(row < rlen){
54615                 if(!first){
54616                     col = 0;
54617                 }
54618                 first = false;
54619                 while(col < clen){
54620                     if(fn.call(scope || this, row, col, cm) === true){
54621                         return [row, col];
54622                     }
54623                     col++;
54624                 }
54625                 row++;
54626             }
54627         }
54628         return null;
54629     },
54630
54631     // private
54632     getSelections : function(){
54633         return this.selModel.getSelections();
54634     },
54635
54636     /**
54637      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
54638      * but if manual update is required this method will initiate it.
54639      */
54640     autoSize : function(){
54641         if(this.rendered){
54642             this.view.layout();
54643             if(this.view.adjustForScroll){
54644                 this.view.adjustForScroll();
54645             }
54646         }
54647     },
54648
54649     /**
54650      * Returns the grid's underlying element.
54651      * @return {Element} The element
54652      */
54653     getGridEl : function(){
54654         return this.container;
54655     },
54656
54657     // private for compatibility, overridden by editor grid
54658     stopEditing : function(){},
54659
54660     /**
54661      * Returns the grid's SelectionModel.
54662      * @return {SelectionModel}
54663      */
54664     getSelectionModel : function(){
54665         if(!this.selModel){
54666             this.selModel = new Roo.grid.RowSelectionModel();
54667         }
54668         return this.selModel;
54669     },
54670
54671     /**
54672      * Returns the grid's DataSource.
54673      * @return {DataSource}
54674      */
54675     getDataSource : function(){
54676         return this.dataSource;
54677     },
54678
54679     /**
54680      * Returns the grid's ColumnModel.
54681      * @return {ColumnModel}
54682      */
54683     getColumnModel : function(){
54684         return this.colModel;
54685     },
54686
54687     /**
54688      * Returns the grid's GridView object.
54689      * @return {GridView}
54690      */
54691     getView : function(){
54692         if(!this.view){
54693             this.view = new Roo.grid.GridView(this.viewConfig);
54694         }
54695         return this.view;
54696     },
54697     /**
54698      * Called to get grid's drag proxy text, by default returns this.ddText.
54699      * @return {String}
54700      */
54701     getDragDropText : function(){
54702         var count = this.selModel.getCount();
54703         return String.format(this.ddText, count, count == 1 ? '' : 's');
54704     }
54705 });
54706 /**
54707  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
54708  * %0 is replaced with the number of selected rows.
54709  * @type String
54710  */
54711 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
54712  * Based on:
54713  * Ext JS Library 1.1.1
54714  * Copyright(c) 2006-2007, Ext JS, LLC.
54715  *
54716  * Originally Released Under LGPL - original licence link has changed is not relivant.
54717  *
54718  * Fork - LGPL
54719  * <script type="text/javascript">
54720  */
54721  
54722 Roo.grid.AbstractGridView = function(){
54723         this.grid = null;
54724         
54725         this.events = {
54726             "beforerowremoved" : true,
54727             "beforerowsinserted" : true,
54728             "beforerefresh" : true,
54729             "rowremoved" : true,
54730             "rowsinserted" : true,
54731             "rowupdated" : true,
54732             "refresh" : true
54733         };
54734     Roo.grid.AbstractGridView.superclass.constructor.call(this);
54735 };
54736
54737 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
54738     rowClass : "x-grid-row",
54739     cellClass : "x-grid-cell",
54740     tdClass : "x-grid-td",
54741     hdClass : "x-grid-hd",
54742     splitClass : "x-grid-hd-split",
54743     
54744     init: function(grid){
54745         this.grid = grid;
54746                 var cid = this.grid.getGridEl().id;
54747         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
54748         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
54749         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
54750         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
54751         },
54752         
54753     getColumnRenderers : function(){
54754         var renderers = [];
54755         var cm = this.grid.colModel;
54756         var colCount = cm.getColumnCount();
54757         for(var i = 0; i < colCount; i++){
54758             renderers[i] = cm.getRenderer(i);
54759         }
54760         return renderers;
54761     },
54762     
54763     getColumnIds : function(){
54764         var ids = [];
54765         var cm = this.grid.colModel;
54766         var colCount = cm.getColumnCount();
54767         for(var i = 0; i < colCount; i++){
54768             ids[i] = cm.getColumnId(i);
54769         }
54770         return ids;
54771     },
54772     
54773     getDataIndexes : function(){
54774         if(!this.indexMap){
54775             this.indexMap = this.buildIndexMap();
54776         }
54777         return this.indexMap.colToData;
54778     },
54779     
54780     getColumnIndexByDataIndex : function(dataIndex){
54781         if(!this.indexMap){
54782             this.indexMap = this.buildIndexMap();
54783         }
54784         return this.indexMap.dataToCol[dataIndex];
54785     },
54786     
54787     /**
54788      * Set a css style for a column dynamically. 
54789      * @param {Number} colIndex The index of the column
54790      * @param {String} name The css property name
54791      * @param {String} value The css value
54792      */
54793     setCSSStyle : function(colIndex, name, value){
54794         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
54795         Roo.util.CSS.updateRule(selector, name, value);
54796     },
54797     
54798     generateRules : function(cm){
54799         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
54800         Roo.util.CSS.removeStyleSheet(rulesId);
54801         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54802             var cid = cm.getColumnId(i);
54803             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
54804                          this.tdSelector, cid, " {\n}\n",
54805                          this.hdSelector, cid, " {\n}\n",
54806                          this.splitSelector, cid, " {\n}\n");
54807         }
54808         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54809     }
54810 });/*
54811  * Based on:
54812  * Ext JS Library 1.1.1
54813  * Copyright(c) 2006-2007, Ext JS, LLC.
54814  *
54815  * Originally Released Under LGPL - original licence link has changed is not relivant.
54816  *
54817  * Fork - LGPL
54818  * <script type="text/javascript">
54819  */
54820
54821 // private
54822 // This is a support class used internally by the Grid components
54823 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
54824     this.grid = grid;
54825     this.view = grid.getView();
54826     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54827     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
54828     if(hd2){
54829         this.setHandleElId(Roo.id(hd));
54830         this.setOuterHandleElId(Roo.id(hd2));
54831     }
54832     this.scroll = false;
54833 };
54834 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
54835     maxDragWidth: 120,
54836     getDragData : function(e){
54837         var t = Roo.lib.Event.getTarget(e);
54838         var h = this.view.findHeaderCell(t);
54839         if(h){
54840             return {ddel: h.firstChild, header:h};
54841         }
54842         return false;
54843     },
54844
54845     onInitDrag : function(e){
54846         this.view.headersDisabled = true;
54847         var clone = this.dragData.ddel.cloneNode(true);
54848         clone.id = Roo.id();
54849         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
54850         this.proxy.update(clone);
54851         return true;
54852     },
54853
54854     afterValidDrop : function(){
54855         var v = this.view;
54856         setTimeout(function(){
54857             v.headersDisabled = false;
54858         }, 50);
54859     },
54860
54861     afterInvalidDrop : function(){
54862         var v = this.view;
54863         setTimeout(function(){
54864             v.headersDisabled = false;
54865         }, 50);
54866     }
54867 });
54868 /*
54869  * Based on:
54870  * Ext JS Library 1.1.1
54871  * Copyright(c) 2006-2007, Ext JS, LLC.
54872  *
54873  * Originally Released Under LGPL - original licence link has changed is not relivant.
54874  *
54875  * Fork - LGPL
54876  * <script type="text/javascript">
54877  */
54878 // private
54879 // This is a support class used internally by the Grid components
54880 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
54881     this.grid = grid;
54882     this.view = grid.getView();
54883     // split the proxies so they don't interfere with mouse events
54884     this.proxyTop = Roo.DomHelper.append(document.body, {
54885         cls:"col-move-top", html:"&#160;"
54886     }, true);
54887     this.proxyBottom = Roo.DomHelper.append(document.body, {
54888         cls:"col-move-bottom", html:"&#160;"
54889     }, true);
54890     this.proxyTop.hide = this.proxyBottom.hide = function(){
54891         this.setLeftTop(-100,-100);
54892         this.setStyle("visibility", "hidden");
54893     };
54894     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54895     // temporarily disabled
54896     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
54897     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
54898 };
54899 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
54900     proxyOffsets : [-4, -9],
54901     fly: Roo.Element.fly,
54902
54903     getTargetFromEvent : function(e){
54904         var t = Roo.lib.Event.getTarget(e);
54905         var cindex = this.view.findCellIndex(t);
54906         if(cindex !== false){
54907             return this.view.getHeaderCell(cindex);
54908         }
54909         return null;
54910     },
54911
54912     nextVisible : function(h){
54913         var v = this.view, cm = this.grid.colModel;
54914         h = h.nextSibling;
54915         while(h){
54916             if(!cm.isHidden(v.getCellIndex(h))){
54917                 return h;
54918             }
54919             h = h.nextSibling;
54920         }
54921         return null;
54922     },
54923
54924     prevVisible : function(h){
54925         var v = this.view, cm = this.grid.colModel;
54926         h = h.prevSibling;
54927         while(h){
54928             if(!cm.isHidden(v.getCellIndex(h))){
54929                 return h;
54930             }
54931             h = h.prevSibling;
54932         }
54933         return null;
54934     },
54935
54936     positionIndicator : function(h, n, e){
54937         var x = Roo.lib.Event.getPageX(e);
54938         var r = Roo.lib.Dom.getRegion(n.firstChild);
54939         var px, pt, py = r.top + this.proxyOffsets[1];
54940         if((r.right - x) <= (r.right-r.left)/2){
54941             px = r.right+this.view.borderWidth;
54942             pt = "after";
54943         }else{
54944             px = r.left;
54945             pt = "before";
54946         }
54947         var oldIndex = this.view.getCellIndex(h);
54948         var newIndex = this.view.getCellIndex(n);
54949
54950         if(this.grid.colModel.isFixed(newIndex)){
54951             return false;
54952         }
54953
54954         var locked = this.grid.colModel.isLocked(newIndex);
54955
54956         if(pt == "after"){
54957             newIndex++;
54958         }
54959         if(oldIndex < newIndex){
54960             newIndex--;
54961         }
54962         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
54963             return false;
54964         }
54965         px +=  this.proxyOffsets[0];
54966         this.proxyTop.setLeftTop(px, py);
54967         this.proxyTop.show();
54968         if(!this.bottomOffset){
54969             this.bottomOffset = this.view.mainHd.getHeight();
54970         }
54971         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
54972         this.proxyBottom.show();
54973         return pt;
54974     },
54975
54976     onNodeEnter : function(n, dd, e, data){
54977         if(data.header != n){
54978             this.positionIndicator(data.header, n, e);
54979         }
54980     },
54981
54982     onNodeOver : function(n, dd, e, data){
54983         var result = false;
54984         if(data.header != n){
54985             result = this.positionIndicator(data.header, n, e);
54986         }
54987         if(!result){
54988             this.proxyTop.hide();
54989             this.proxyBottom.hide();
54990         }
54991         return result ? this.dropAllowed : this.dropNotAllowed;
54992     },
54993
54994     onNodeOut : function(n, dd, e, data){
54995         this.proxyTop.hide();
54996         this.proxyBottom.hide();
54997     },
54998
54999     onNodeDrop : function(n, dd, e, data){
55000         var h = data.header;
55001         if(h != n){
55002             var cm = this.grid.colModel;
55003             var x = Roo.lib.Event.getPageX(e);
55004             var r = Roo.lib.Dom.getRegion(n.firstChild);
55005             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
55006             var oldIndex = this.view.getCellIndex(h);
55007             var newIndex = this.view.getCellIndex(n);
55008             var locked = cm.isLocked(newIndex);
55009             if(pt == "after"){
55010                 newIndex++;
55011             }
55012             if(oldIndex < newIndex){
55013                 newIndex--;
55014             }
55015             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
55016                 return false;
55017             }
55018             cm.setLocked(oldIndex, locked, true);
55019             cm.moveColumn(oldIndex, newIndex);
55020             this.grid.fireEvent("columnmove", oldIndex, newIndex);
55021             return true;
55022         }
55023         return false;
55024     }
55025 });
55026 /*
55027  * Based on:
55028  * Ext JS Library 1.1.1
55029  * Copyright(c) 2006-2007, Ext JS, LLC.
55030  *
55031  * Originally Released Under LGPL - original licence link has changed is not relivant.
55032  *
55033  * Fork - LGPL
55034  * <script type="text/javascript">
55035  */
55036   
55037 /**
55038  * @class Roo.grid.GridView
55039  * @extends Roo.util.Observable
55040  *
55041  * @constructor
55042  * @param {Object} config
55043  */
55044 Roo.grid.GridView = function(config){
55045     Roo.grid.GridView.superclass.constructor.call(this);
55046     this.el = null;
55047
55048     Roo.apply(this, config);
55049 };
55050
55051 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
55052
55053     unselectable :  'unselectable="on"',
55054     unselectableCls :  'x-unselectable',
55055     
55056     
55057     rowClass : "x-grid-row",
55058
55059     cellClass : "x-grid-col",
55060
55061     tdClass : "x-grid-td",
55062
55063     hdClass : "x-grid-hd",
55064
55065     splitClass : "x-grid-split",
55066
55067     sortClasses : ["sort-asc", "sort-desc"],
55068
55069     enableMoveAnim : false,
55070
55071     hlColor: "C3DAF9",
55072
55073     dh : Roo.DomHelper,
55074
55075     fly : Roo.Element.fly,
55076
55077     css : Roo.util.CSS,
55078
55079     borderWidth: 1,
55080
55081     splitOffset: 3,
55082
55083     scrollIncrement : 22,
55084
55085     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
55086
55087     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
55088
55089     bind : function(ds, cm){
55090         if(this.ds){
55091             this.ds.un("load", this.onLoad, this);
55092             this.ds.un("datachanged", this.onDataChange, this);
55093             this.ds.un("add", this.onAdd, this);
55094             this.ds.un("remove", this.onRemove, this);
55095             this.ds.un("update", this.onUpdate, this);
55096             this.ds.un("clear", this.onClear, this);
55097         }
55098         if(ds){
55099             ds.on("load", this.onLoad, this);
55100             ds.on("datachanged", this.onDataChange, this);
55101             ds.on("add", this.onAdd, this);
55102             ds.on("remove", this.onRemove, this);
55103             ds.on("update", this.onUpdate, this);
55104             ds.on("clear", this.onClear, this);
55105         }
55106         this.ds = ds;
55107
55108         if(this.cm){
55109             this.cm.un("widthchange", this.onColWidthChange, this);
55110             this.cm.un("headerchange", this.onHeaderChange, this);
55111             this.cm.un("hiddenchange", this.onHiddenChange, this);
55112             this.cm.un("columnmoved", this.onColumnMove, this);
55113             this.cm.un("columnlockchange", this.onColumnLock, this);
55114         }
55115         if(cm){
55116             this.generateRules(cm);
55117             cm.on("widthchange", this.onColWidthChange, this);
55118             cm.on("headerchange", this.onHeaderChange, this);
55119             cm.on("hiddenchange", this.onHiddenChange, this);
55120             cm.on("columnmoved", this.onColumnMove, this);
55121             cm.on("columnlockchange", this.onColumnLock, this);
55122         }
55123         this.cm = cm;
55124     },
55125
55126     init: function(grid){
55127         Roo.grid.GridView.superclass.init.call(this, grid);
55128
55129         this.bind(grid.dataSource, grid.colModel);
55130
55131         grid.on("headerclick", this.handleHeaderClick, this);
55132
55133         if(grid.trackMouseOver){
55134             grid.on("mouseover", this.onRowOver, this);
55135             grid.on("mouseout", this.onRowOut, this);
55136         }
55137         grid.cancelTextSelection = function(){};
55138         this.gridId = grid.id;
55139
55140         var tpls = this.templates || {};
55141
55142         if(!tpls.master){
55143             tpls.master = new Roo.Template(
55144                '<div class="x-grid" hidefocus="true">',
55145                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
55146                   '<div class="x-grid-topbar"></div>',
55147                   '<div class="x-grid-scroller"><div></div></div>',
55148                   '<div class="x-grid-locked">',
55149                       '<div class="x-grid-header">{lockedHeader}</div>',
55150                       '<div class="x-grid-body">{lockedBody}</div>',
55151                   "</div>",
55152                   '<div class="x-grid-viewport">',
55153                       '<div class="x-grid-header">{header}</div>',
55154                       '<div class="x-grid-body">{body}</div>',
55155                   "</div>",
55156                   '<div class="x-grid-bottombar"></div>',
55157                  
55158                   '<div class="x-grid-resize-proxy">&#160;</div>',
55159                "</div>"
55160             );
55161             tpls.master.disableformats = true;
55162         }
55163
55164         if(!tpls.header){
55165             tpls.header = new Roo.Template(
55166                '<table border="0" cellspacing="0" cellpadding="0">',
55167                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
55168                "</table>{splits}"
55169             );
55170             tpls.header.disableformats = true;
55171         }
55172         tpls.header.compile();
55173
55174         if(!tpls.hcell){
55175             tpls.hcell = new Roo.Template(
55176                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
55177                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
55178                 "</div></td>"
55179              );
55180              tpls.hcell.disableFormats = true;
55181         }
55182         tpls.hcell.compile();
55183
55184         if(!tpls.hsplit){
55185             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
55186                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
55187             tpls.hsplit.disableFormats = true;
55188         }
55189         tpls.hsplit.compile();
55190
55191         if(!tpls.body){
55192             tpls.body = new Roo.Template(
55193                '<table border="0" cellspacing="0" cellpadding="0">',
55194                "<tbody>{rows}</tbody>",
55195                "</table>"
55196             );
55197             tpls.body.disableFormats = true;
55198         }
55199         tpls.body.compile();
55200
55201         if(!tpls.row){
55202             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
55203             tpls.row.disableFormats = true;
55204         }
55205         tpls.row.compile();
55206
55207         if(!tpls.cell){
55208             tpls.cell = new Roo.Template(
55209                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
55210                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
55211                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
55212                 "</td>"
55213             );
55214             tpls.cell.disableFormats = true;
55215         }
55216         tpls.cell.compile();
55217
55218         this.templates = tpls;
55219     },
55220
55221     // remap these for backwards compat
55222     onColWidthChange : function(){
55223         this.updateColumns.apply(this, arguments);
55224     },
55225     onHeaderChange : function(){
55226         this.updateHeaders.apply(this, arguments);
55227     }, 
55228     onHiddenChange : function(){
55229         this.handleHiddenChange.apply(this, arguments);
55230     },
55231     onColumnMove : function(){
55232         this.handleColumnMove.apply(this, arguments);
55233     },
55234     onColumnLock : function(){
55235         this.handleLockChange.apply(this, arguments);
55236     },
55237
55238     onDataChange : function(){
55239         this.refresh();
55240         this.updateHeaderSortState();
55241     },
55242
55243     onClear : function(){
55244         this.refresh();
55245     },
55246
55247     onUpdate : function(ds, record){
55248         this.refreshRow(record);
55249     },
55250
55251     refreshRow : function(record){
55252         var ds = this.ds, index;
55253         if(typeof record == 'number'){
55254             index = record;
55255             record = ds.getAt(index);
55256         }else{
55257             index = ds.indexOf(record);
55258         }
55259         this.insertRows(ds, index, index, true);
55260         this.onRemove(ds, record, index+1, true);
55261         this.syncRowHeights(index, index);
55262         this.layout();
55263         this.fireEvent("rowupdated", this, index, record);
55264     },
55265
55266     onAdd : function(ds, records, index){
55267         this.insertRows(ds, index, index + (records.length-1));
55268     },
55269
55270     onRemove : function(ds, record, index, isUpdate){
55271         if(isUpdate !== true){
55272             this.fireEvent("beforerowremoved", this, index, record);
55273         }
55274         var bt = this.getBodyTable(), lt = this.getLockedTable();
55275         if(bt.rows[index]){
55276             bt.firstChild.removeChild(bt.rows[index]);
55277         }
55278         if(lt.rows[index]){
55279             lt.firstChild.removeChild(lt.rows[index]);
55280         }
55281         if(isUpdate !== true){
55282             this.stripeRows(index);
55283             this.syncRowHeights(index, index);
55284             this.layout();
55285             this.fireEvent("rowremoved", this, index, record);
55286         }
55287     },
55288
55289     onLoad : function(){
55290         this.scrollToTop();
55291     },
55292
55293     /**
55294      * Scrolls the grid to the top
55295      */
55296     scrollToTop : function(){
55297         if(this.scroller){
55298             this.scroller.dom.scrollTop = 0;
55299             this.syncScroll();
55300         }
55301     },
55302
55303     /**
55304      * Gets a panel in the header of the grid that can be used for toolbars etc.
55305      * After modifying the contents of this panel a call to grid.autoSize() may be
55306      * required to register any changes in size.
55307      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
55308      * @return Roo.Element
55309      */
55310     getHeaderPanel : function(doShow){
55311         if(doShow){
55312             this.headerPanel.show();
55313         }
55314         return this.headerPanel;
55315     },
55316
55317     /**
55318      * Gets a panel in the footer of the grid that can be used for toolbars etc.
55319      * After modifying the contents of this panel a call to grid.autoSize() may be
55320      * required to register any changes in size.
55321      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
55322      * @return Roo.Element
55323      */
55324     getFooterPanel : function(doShow){
55325         if(doShow){
55326             this.footerPanel.show();
55327         }
55328         return this.footerPanel;
55329     },
55330
55331     initElements : function(){
55332         var E = Roo.Element;
55333         var el = this.grid.getGridEl().dom.firstChild;
55334         var cs = el.childNodes;
55335
55336         this.el = new E(el);
55337         
55338          this.focusEl = new E(el.firstChild);
55339         this.focusEl.swallowEvent("click", true);
55340         
55341         this.headerPanel = new E(cs[1]);
55342         this.headerPanel.enableDisplayMode("block");
55343
55344         this.scroller = new E(cs[2]);
55345         this.scrollSizer = new E(this.scroller.dom.firstChild);
55346
55347         this.lockedWrap = new E(cs[3]);
55348         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
55349         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
55350
55351         this.mainWrap = new E(cs[4]);
55352         this.mainHd = new E(this.mainWrap.dom.firstChild);
55353         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
55354
55355         this.footerPanel = new E(cs[5]);
55356         this.footerPanel.enableDisplayMode("block");
55357
55358         this.resizeProxy = new E(cs[6]);
55359
55360         this.headerSelector = String.format(
55361            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
55362            this.lockedHd.id, this.mainHd.id
55363         );
55364
55365         this.splitterSelector = String.format(
55366            '#{0} div.x-grid-split, #{1} div.x-grid-split',
55367            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
55368         );
55369     },
55370     idToCssName : function(s)
55371     {
55372         return s.replace(/[^a-z0-9]+/ig, '-');
55373     },
55374
55375     getHeaderCell : function(index){
55376         return Roo.DomQuery.select(this.headerSelector)[index];
55377     },
55378
55379     getHeaderCellMeasure : function(index){
55380         return this.getHeaderCell(index).firstChild;
55381     },
55382
55383     getHeaderCellText : function(index){
55384         return this.getHeaderCell(index).firstChild.firstChild;
55385     },
55386
55387     getLockedTable : function(){
55388         return this.lockedBody.dom.firstChild;
55389     },
55390
55391     getBodyTable : function(){
55392         return this.mainBody.dom.firstChild;
55393     },
55394
55395     getLockedRow : function(index){
55396         return this.getLockedTable().rows[index];
55397     },
55398
55399     getRow : function(index){
55400         return this.getBodyTable().rows[index];
55401     },
55402
55403     getRowComposite : function(index){
55404         if(!this.rowEl){
55405             this.rowEl = new Roo.CompositeElementLite();
55406         }
55407         var els = [], lrow, mrow;
55408         if(lrow = this.getLockedRow(index)){
55409             els.push(lrow);
55410         }
55411         if(mrow = this.getRow(index)){
55412             els.push(mrow);
55413         }
55414         this.rowEl.elements = els;
55415         return this.rowEl;
55416     },
55417     /**
55418      * Gets the 'td' of the cell
55419      * 
55420      * @param {Integer} rowIndex row to select
55421      * @param {Integer} colIndex column to select
55422      * 
55423      * @return {Object} 
55424      */
55425     getCell : function(rowIndex, colIndex){
55426         var locked = this.cm.getLockedCount();
55427         var source;
55428         if(colIndex < locked){
55429             source = this.lockedBody.dom.firstChild;
55430         }else{
55431             source = this.mainBody.dom.firstChild;
55432             colIndex -= locked;
55433         }
55434         return source.rows[rowIndex].childNodes[colIndex];
55435     },
55436
55437     getCellText : function(rowIndex, colIndex){
55438         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
55439     },
55440
55441     getCellBox : function(cell){
55442         var b = this.fly(cell).getBox();
55443         if(Roo.isOpera){ // opera fails to report the Y
55444             b.y = cell.offsetTop + this.mainBody.getY();
55445         }
55446         return b;
55447     },
55448
55449     getCellIndex : function(cell){
55450         var id = String(cell.className).match(this.cellRE);
55451         if(id){
55452             return parseInt(id[1], 10);
55453         }
55454         return 0;
55455     },
55456
55457     findHeaderIndex : function(n){
55458         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55459         return r ? this.getCellIndex(r) : false;
55460     },
55461
55462     findHeaderCell : function(n){
55463         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55464         return r ? r : false;
55465     },
55466
55467     findRowIndex : function(n){
55468         if(!n){
55469             return false;
55470         }
55471         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
55472         return r ? r.rowIndex : false;
55473     },
55474
55475     findCellIndex : function(node){
55476         var stop = this.el.dom;
55477         while(node && node != stop){
55478             if(this.findRE.test(node.className)){
55479                 return this.getCellIndex(node);
55480             }
55481             node = node.parentNode;
55482         }
55483         return false;
55484     },
55485
55486     getColumnId : function(index){
55487         return this.cm.getColumnId(index);
55488     },
55489
55490     getSplitters : function()
55491     {
55492         if(this.splitterSelector){
55493            return Roo.DomQuery.select(this.splitterSelector);
55494         }else{
55495             return null;
55496       }
55497     },
55498
55499     getSplitter : function(index){
55500         return this.getSplitters()[index];
55501     },
55502
55503     onRowOver : function(e, t){
55504         var row;
55505         if((row = this.findRowIndex(t)) !== false){
55506             this.getRowComposite(row).addClass("x-grid-row-over");
55507         }
55508     },
55509
55510     onRowOut : function(e, t){
55511         var row;
55512         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
55513             this.getRowComposite(row).removeClass("x-grid-row-over");
55514         }
55515     },
55516
55517     renderHeaders : function(){
55518         var cm = this.cm;
55519         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
55520         var cb = [], lb = [], sb = [], lsb = [], p = {};
55521         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55522             p.cellId = "x-grid-hd-0-" + i;
55523             p.splitId = "x-grid-csplit-0-" + i;
55524             p.id = cm.getColumnId(i);
55525             p.value = cm.getColumnHeader(i) || "";
55526             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
55527             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
55528             if(!cm.isLocked(i)){
55529                 cb[cb.length] = ct.apply(p);
55530                 sb[sb.length] = st.apply(p);
55531             }else{
55532                 lb[lb.length] = ct.apply(p);
55533                 lsb[lsb.length] = st.apply(p);
55534             }
55535         }
55536         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
55537                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
55538     },
55539
55540     updateHeaders : function(){
55541         var html = this.renderHeaders();
55542         this.lockedHd.update(html[0]);
55543         this.mainHd.update(html[1]);
55544     },
55545
55546     /**
55547      * Focuses the specified row.
55548      * @param {Number} row The row index
55549      */
55550     focusRow : function(row)
55551     {
55552         //Roo.log('GridView.focusRow');
55553         var x = this.scroller.dom.scrollLeft;
55554         this.focusCell(row, 0, false);
55555         this.scroller.dom.scrollLeft = x;
55556     },
55557
55558     /**
55559      * Focuses the specified cell.
55560      * @param {Number} row The row index
55561      * @param {Number} col The column index
55562      * @param {Boolean} hscroll false to disable horizontal scrolling
55563      */
55564     focusCell : function(row, col, hscroll)
55565     {
55566         //Roo.log('GridView.focusCell');
55567         var el = this.ensureVisible(row, col, hscroll);
55568         this.focusEl.alignTo(el, "tl-tl");
55569         if(Roo.isGecko){
55570             this.focusEl.focus();
55571         }else{
55572             this.focusEl.focus.defer(1, this.focusEl);
55573         }
55574     },
55575
55576     /**
55577      * Scrolls the specified cell into view
55578      * @param {Number} row The row index
55579      * @param {Number} col The column index
55580      * @param {Boolean} hscroll false to disable horizontal scrolling
55581      */
55582     ensureVisible : function(row, col, hscroll)
55583     {
55584         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
55585         //return null; //disable for testing.
55586         if(typeof row != "number"){
55587             row = row.rowIndex;
55588         }
55589         if(row < 0 && row >= this.ds.getCount()){
55590             return  null;
55591         }
55592         col = (col !== undefined ? col : 0);
55593         var cm = this.grid.colModel;
55594         while(cm.isHidden(col)){
55595             col++;
55596         }
55597
55598         var el = this.getCell(row, col);
55599         if(!el){
55600             return null;
55601         }
55602         var c = this.scroller.dom;
55603
55604         var ctop = parseInt(el.offsetTop, 10);
55605         var cleft = parseInt(el.offsetLeft, 10);
55606         var cbot = ctop + el.offsetHeight;
55607         var cright = cleft + el.offsetWidth;
55608         
55609         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
55610         var stop = parseInt(c.scrollTop, 10);
55611         var sleft = parseInt(c.scrollLeft, 10);
55612         var sbot = stop + ch;
55613         var sright = sleft + c.clientWidth;
55614         /*
55615         Roo.log('GridView.ensureVisible:' +
55616                 ' ctop:' + ctop +
55617                 ' c.clientHeight:' + c.clientHeight +
55618                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
55619                 ' stop:' + stop +
55620                 ' cbot:' + cbot +
55621                 ' sbot:' + sbot +
55622                 ' ch:' + ch  
55623                 );
55624         */
55625         if(ctop < stop){
55626              c.scrollTop = ctop;
55627             //Roo.log("set scrolltop to ctop DISABLE?");
55628         }else if(cbot > sbot){
55629             //Roo.log("set scrolltop to cbot-ch");
55630             c.scrollTop = cbot-ch;
55631         }
55632         
55633         if(hscroll !== false){
55634             if(cleft < sleft){
55635                 c.scrollLeft = cleft;
55636             }else if(cright > sright){
55637                 c.scrollLeft = cright-c.clientWidth;
55638             }
55639         }
55640          
55641         return el;
55642     },
55643
55644     updateColumns : function(){
55645         this.grid.stopEditing();
55646         var cm = this.grid.colModel, colIds = this.getColumnIds();
55647         //var totalWidth = cm.getTotalWidth();
55648         var pos = 0;
55649         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55650             //if(cm.isHidden(i)) continue;
55651             var w = cm.getColumnWidth(i);
55652             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55653             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55654         }
55655         this.updateSplitters();
55656     },
55657
55658     generateRules : function(cm){
55659         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
55660         Roo.util.CSS.removeStyleSheet(rulesId);
55661         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55662             var cid = cm.getColumnId(i);
55663             var align = '';
55664             if(cm.config[i].align){
55665                 align = 'text-align:'+cm.config[i].align+';';
55666             }
55667             var hidden = '';
55668             if(cm.isHidden(i)){
55669                 hidden = 'display:none;';
55670             }
55671             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
55672             ruleBuf.push(
55673                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
55674                     this.hdSelector, cid, " {\n", align, width, "}\n",
55675                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
55676                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
55677         }
55678         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55679     },
55680
55681     updateSplitters : function(){
55682         var cm = this.cm, s = this.getSplitters();
55683         if(s){ // splitters not created yet
55684             var pos = 0, locked = true;
55685             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55686                 if(cm.isHidden(i)) {
55687                     continue;
55688                 }
55689                 var w = cm.getColumnWidth(i); // make sure it's a number
55690                 if(!cm.isLocked(i) && locked){
55691                     pos = 0;
55692                     locked = false;
55693                 }
55694                 pos += w;
55695                 s[i].style.left = (pos-this.splitOffset) + "px";
55696             }
55697         }
55698     },
55699
55700     handleHiddenChange : function(colModel, colIndex, hidden){
55701         if(hidden){
55702             this.hideColumn(colIndex);
55703         }else{
55704             this.unhideColumn(colIndex);
55705         }
55706     },
55707
55708     hideColumn : function(colIndex){
55709         var cid = this.getColumnId(colIndex);
55710         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
55711         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
55712         if(Roo.isSafari){
55713             this.updateHeaders();
55714         }
55715         this.updateSplitters();
55716         this.layout();
55717     },
55718
55719     unhideColumn : function(colIndex){
55720         var cid = this.getColumnId(colIndex);
55721         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
55722         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
55723
55724         if(Roo.isSafari){
55725             this.updateHeaders();
55726         }
55727         this.updateSplitters();
55728         this.layout();
55729     },
55730
55731     insertRows : function(dm, firstRow, lastRow, isUpdate){
55732         if(firstRow == 0 && lastRow == dm.getCount()-1){
55733             this.refresh();
55734         }else{
55735             if(!isUpdate){
55736                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
55737             }
55738             var s = this.getScrollState();
55739             var markup = this.renderRows(firstRow, lastRow);
55740             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
55741             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
55742             this.restoreScroll(s);
55743             if(!isUpdate){
55744                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
55745                 this.syncRowHeights(firstRow, lastRow);
55746                 this.stripeRows(firstRow);
55747                 this.layout();
55748             }
55749         }
55750     },
55751
55752     bufferRows : function(markup, target, index){
55753         var before = null, trows = target.rows, tbody = target.tBodies[0];
55754         if(index < trows.length){
55755             before = trows[index];
55756         }
55757         var b = document.createElement("div");
55758         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
55759         var rows = b.firstChild.rows;
55760         for(var i = 0, len = rows.length; i < len; i++){
55761             if(before){
55762                 tbody.insertBefore(rows[0], before);
55763             }else{
55764                 tbody.appendChild(rows[0]);
55765             }
55766         }
55767         b.innerHTML = "";
55768         b = null;
55769     },
55770
55771     deleteRows : function(dm, firstRow, lastRow){
55772         if(dm.getRowCount()<1){
55773             this.fireEvent("beforerefresh", this);
55774             this.mainBody.update("");
55775             this.lockedBody.update("");
55776             this.fireEvent("refresh", this);
55777         }else{
55778             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
55779             var bt = this.getBodyTable();
55780             var tbody = bt.firstChild;
55781             var rows = bt.rows;
55782             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
55783                 tbody.removeChild(rows[firstRow]);
55784             }
55785             this.stripeRows(firstRow);
55786             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
55787         }
55788     },
55789
55790     updateRows : function(dataSource, firstRow, lastRow){
55791         var s = this.getScrollState();
55792         this.refresh();
55793         this.restoreScroll(s);
55794     },
55795
55796     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
55797         if(!noRefresh){
55798            this.refresh();
55799         }
55800         this.updateHeaderSortState();
55801     },
55802
55803     getScrollState : function(){
55804         
55805         var sb = this.scroller.dom;
55806         return {left: sb.scrollLeft, top: sb.scrollTop};
55807     },
55808
55809     stripeRows : function(startRow){
55810         if(!this.grid.stripeRows || this.ds.getCount() < 1){
55811             return;
55812         }
55813         startRow = startRow || 0;
55814         var rows = this.getBodyTable().rows;
55815         var lrows = this.getLockedTable().rows;
55816         var cls = ' x-grid-row-alt ';
55817         for(var i = startRow, len = rows.length; i < len; i++){
55818             var row = rows[i], lrow = lrows[i];
55819             var isAlt = ((i+1) % 2 == 0);
55820             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
55821             if(isAlt == hasAlt){
55822                 continue;
55823             }
55824             if(isAlt){
55825                 row.className += " x-grid-row-alt";
55826             }else{
55827                 row.className = row.className.replace("x-grid-row-alt", "");
55828             }
55829             if(lrow){
55830                 lrow.className = row.className;
55831             }
55832         }
55833     },
55834
55835     restoreScroll : function(state){
55836         //Roo.log('GridView.restoreScroll');
55837         var sb = this.scroller.dom;
55838         sb.scrollLeft = state.left;
55839         sb.scrollTop = state.top;
55840         this.syncScroll();
55841     },
55842
55843     syncScroll : function(){
55844         //Roo.log('GridView.syncScroll');
55845         var sb = this.scroller.dom;
55846         var sh = this.mainHd.dom;
55847         var bs = this.mainBody.dom;
55848         var lv = this.lockedBody.dom;
55849         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
55850         lv.scrollTop = bs.scrollTop = sb.scrollTop;
55851     },
55852
55853     handleScroll : function(e){
55854         this.syncScroll();
55855         var sb = this.scroller.dom;
55856         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
55857         e.stopEvent();
55858     },
55859
55860     handleWheel : function(e){
55861         var d = e.getWheelDelta();
55862         this.scroller.dom.scrollTop -= d*22;
55863         // set this here to prevent jumpy scrolling on large tables
55864         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
55865         e.stopEvent();
55866     },
55867
55868     renderRows : function(startRow, endRow){
55869         // pull in all the crap needed to render rows
55870         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
55871         var colCount = cm.getColumnCount();
55872
55873         if(ds.getCount() < 1){
55874             return ["", ""];
55875         }
55876
55877         // build a map for all the columns
55878         var cs = [];
55879         for(var i = 0; i < colCount; i++){
55880             var name = cm.getDataIndex(i);
55881             cs[i] = {
55882                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
55883                 renderer : cm.getRenderer(i),
55884                 id : cm.getColumnId(i),
55885                 locked : cm.isLocked(i),
55886                 has_editor : cm.isCellEditable(i)
55887             };
55888         }
55889
55890         startRow = startRow || 0;
55891         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
55892
55893         // records to render
55894         var rs = ds.getRange(startRow, endRow);
55895
55896         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
55897     },
55898
55899     // As much as I hate to duplicate code, this was branched because FireFox really hates
55900     // [].join("") on strings. The performance difference was substantial enough to
55901     // branch this function
55902     doRender : Roo.isGecko ?
55903             function(cs, rs, ds, startRow, colCount, stripe){
55904                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55905                 // buffers
55906                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
55907                 
55908                 var hasListener = this.grid.hasListener('rowclass');
55909                 var rowcfg = {};
55910                 for(var j = 0, len = rs.length; j < len; j++){
55911                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
55912                     for(var i = 0; i < colCount; i++){
55913                         c = cs[i];
55914                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
55915                         p.id = c.id;
55916                         p.css = p.attr = "";
55917                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
55918                         if(p.value == undefined || p.value === "") {
55919                             p.value = "&#160;";
55920                         }
55921                         if(c.has_editor){
55922                             p.css += ' x-grid-editable-cell';
55923                         }
55924                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
55925                             p.css +=  ' x-grid-dirty-cell';
55926                         }
55927                         var markup = ct.apply(p);
55928                         if(!c.locked){
55929                             cb+= markup;
55930                         }else{
55931                             lcb+= markup;
55932                         }
55933                     }
55934                     var alt = [];
55935                     if(stripe && ((rowIndex+1) % 2 == 0)){
55936                         alt.push("x-grid-row-alt")
55937                     }
55938                     if(r.dirty){
55939                         alt.push(  " x-grid-dirty-row");
55940                     }
55941                     rp.cells = lcb;
55942                     if(this.getRowClass){
55943                         alt.push(this.getRowClass(r, rowIndex));
55944                     }
55945                     if (hasListener) {
55946                         rowcfg = {
55947                              
55948                             record: r,
55949                             rowIndex : rowIndex,
55950                             rowClass : ''
55951                         };
55952                         this.grid.fireEvent('rowclass', this, rowcfg);
55953                         alt.push(rowcfg.rowClass);
55954                     }
55955                     rp.alt = alt.join(" ");
55956                     lbuf+= rt.apply(rp);
55957                     rp.cells = cb;
55958                     buf+=  rt.apply(rp);
55959                 }
55960                 return [lbuf, buf];
55961             } :
55962             function(cs, rs, ds, startRow, colCount, stripe){
55963                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55964                 // buffers
55965                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
55966                 var hasListener = this.grid.hasListener('rowclass');
55967  
55968                 var rowcfg = {};
55969                 for(var j = 0, len = rs.length; j < len; j++){
55970                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
55971                     for(var i = 0; i < colCount; i++){
55972                         c = cs[i];
55973                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
55974                         p.id = c.id;
55975                         p.css = p.attr = "";
55976                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
55977                         if(p.value == undefined || p.value === "") {
55978                             p.value = "&#160;";
55979                         }
55980                         //Roo.log(c);
55981                          if(c.has_editor){
55982                             p.css += ' x-grid-editable-cell';
55983                         }
55984                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
55985                             p.css += ' x-grid-dirty-cell' 
55986                         }
55987                         
55988                         var markup = ct.apply(p);
55989                         if(!c.locked){
55990                             cb[cb.length] = markup;
55991                         }else{
55992                             lcb[lcb.length] = markup;
55993                         }
55994                     }
55995                     var alt = [];
55996                     if(stripe && ((rowIndex+1) % 2 == 0)){
55997                         alt.push( "x-grid-row-alt");
55998                     }
55999                     if(r.dirty){
56000                         alt.push(" x-grid-dirty-row");
56001                     }
56002                     rp.cells = lcb;
56003                     if(this.getRowClass){
56004                         alt.push( this.getRowClass(r, rowIndex));
56005                     }
56006                     if (hasListener) {
56007                         rowcfg = {
56008                              
56009                             record: r,
56010                             rowIndex : rowIndex,
56011                             rowClass : ''
56012                         };
56013                         this.grid.fireEvent('rowclass', this, rowcfg);
56014                         alt.push(rowcfg.rowClass);
56015                     }
56016                     
56017                     rp.alt = alt.join(" ");
56018                     rp.cells = lcb.join("");
56019                     lbuf[lbuf.length] = rt.apply(rp);
56020                     rp.cells = cb.join("");
56021                     buf[buf.length] =  rt.apply(rp);
56022                 }
56023                 return [lbuf.join(""), buf.join("")];
56024             },
56025
56026     renderBody : function(){
56027         var markup = this.renderRows();
56028         var bt = this.templates.body;
56029         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
56030     },
56031
56032     /**
56033      * Refreshes the grid
56034      * @param {Boolean} headersToo
56035      */
56036     refresh : function(headersToo){
56037         this.fireEvent("beforerefresh", this);
56038         this.grid.stopEditing();
56039         var result = this.renderBody();
56040         this.lockedBody.update(result[0]);
56041         this.mainBody.update(result[1]);
56042         if(headersToo === true){
56043             this.updateHeaders();
56044             this.updateColumns();
56045             this.updateSplitters();
56046             this.updateHeaderSortState();
56047         }
56048         this.syncRowHeights();
56049         this.layout();
56050         this.fireEvent("refresh", this);
56051     },
56052
56053     handleColumnMove : function(cm, oldIndex, newIndex){
56054         this.indexMap = null;
56055         var s = this.getScrollState();
56056         this.refresh(true);
56057         this.restoreScroll(s);
56058         this.afterMove(newIndex);
56059     },
56060
56061     afterMove : function(colIndex){
56062         if(this.enableMoveAnim && Roo.enableFx){
56063             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
56064         }
56065         // if multisort - fix sortOrder, and reload..
56066         if (this.grid.dataSource.multiSort) {
56067             // the we can call sort again..
56068             var dm = this.grid.dataSource;
56069             var cm = this.grid.colModel;
56070             var so = [];
56071             for(var i = 0; i < cm.config.length; i++ ) {
56072                 
56073                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
56074                     continue; // dont' bother, it's not in sort list or being set.
56075                 }
56076                 
56077                 so.push(cm.config[i].dataIndex);
56078             };
56079             dm.sortOrder = so;
56080             dm.load(dm.lastOptions);
56081             
56082             
56083         }
56084         
56085     },
56086
56087     updateCell : function(dm, rowIndex, dataIndex){
56088         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
56089         if(typeof colIndex == "undefined"){ // not present in grid
56090             return;
56091         }
56092         var cm = this.grid.colModel;
56093         var cell = this.getCell(rowIndex, colIndex);
56094         var cellText = this.getCellText(rowIndex, colIndex);
56095
56096         var p = {
56097             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
56098             id : cm.getColumnId(colIndex),
56099             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
56100         };
56101         var renderer = cm.getRenderer(colIndex);
56102         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
56103         if(typeof val == "undefined" || val === "") {
56104             val = "&#160;";
56105         }
56106         cellText.innerHTML = val;
56107         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
56108         this.syncRowHeights(rowIndex, rowIndex);
56109     },
56110
56111     calcColumnWidth : function(colIndex, maxRowsToMeasure){
56112         var maxWidth = 0;
56113         if(this.grid.autoSizeHeaders){
56114             var h = this.getHeaderCellMeasure(colIndex);
56115             maxWidth = Math.max(maxWidth, h.scrollWidth);
56116         }
56117         var tb, index;
56118         if(this.cm.isLocked(colIndex)){
56119             tb = this.getLockedTable();
56120             index = colIndex;
56121         }else{
56122             tb = this.getBodyTable();
56123             index = colIndex - this.cm.getLockedCount();
56124         }
56125         if(tb && tb.rows){
56126             var rows = tb.rows;
56127             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
56128             for(var i = 0; i < stopIndex; i++){
56129                 var cell = rows[i].childNodes[index].firstChild;
56130                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
56131             }
56132         }
56133         return maxWidth + /*margin for error in IE*/ 5;
56134     },
56135     /**
56136      * Autofit a column to its content.
56137      * @param {Number} colIndex
56138      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
56139      */
56140      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
56141          if(this.cm.isHidden(colIndex)){
56142              return; // can't calc a hidden column
56143          }
56144         if(forceMinSize){
56145             var cid = this.cm.getColumnId(colIndex);
56146             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
56147            if(this.grid.autoSizeHeaders){
56148                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
56149            }
56150         }
56151         var newWidth = this.calcColumnWidth(colIndex);
56152         this.cm.setColumnWidth(colIndex,
56153             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
56154         if(!suppressEvent){
56155             this.grid.fireEvent("columnresize", colIndex, newWidth);
56156         }
56157     },
56158
56159     /**
56160      * Autofits all columns to their content and then expands to fit any extra space in the grid
56161      */
56162      autoSizeColumns : function(){
56163         var cm = this.grid.colModel;
56164         var colCount = cm.getColumnCount();
56165         for(var i = 0; i < colCount; i++){
56166             this.autoSizeColumn(i, true, true);
56167         }
56168         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
56169             this.fitColumns();
56170         }else{
56171             this.updateColumns();
56172             this.layout();
56173         }
56174     },
56175
56176     /**
56177      * Autofits all columns to the grid's width proportionate with their current size
56178      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
56179      */
56180     fitColumns : function(reserveScrollSpace){
56181         var cm = this.grid.colModel;
56182         var colCount = cm.getColumnCount();
56183         var cols = [];
56184         var width = 0;
56185         var i, w;
56186         for (i = 0; i < colCount; i++){
56187             if(!cm.isHidden(i) && !cm.isFixed(i)){
56188                 w = cm.getColumnWidth(i);
56189                 cols.push(i);
56190                 cols.push(w);
56191                 width += w;
56192             }
56193         }
56194         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
56195         if(reserveScrollSpace){
56196             avail -= 17;
56197         }
56198         var frac = (avail - cm.getTotalWidth())/width;
56199         while (cols.length){
56200             w = cols.pop();
56201             i = cols.pop();
56202             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
56203         }
56204         this.updateColumns();
56205         this.layout();
56206     },
56207
56208     onRowSelect : function(rowIndex){
56209         var row = this.getRowComposite(rowIndex);
56210         row.addClass("x-grid-row-selected");
56211     },
56212
56213     onRowDeselect : function(rowIndex){
56214         var row = this.getRowComposite(rowIndex);
56215         row.removeClass("x-grid-row-selected");
56216     },
56217
56218     onCellSelect : function(row, col){
56219         var cell = this.getCell(row, col);
56220         if(cell){
56221             Roo.fly(cell).addClass("x-grid-cell-selected");
56222         }
56223     },
56224
56225     onCellDeselect : function(row, col){
56226         var cell = this.getCell(row, col);
56227         if(cell){
56228             Roo.fly(cell).removeClass("x-grid-cell-selected");
56229         }
56230     },
56231
56232     updateHeaderSortState : function(){
56233         
56234         // sort state can be single { field: xxx, direction : yyy}
56235         // or   { xxx=>ASC , yyy : DESC ..... }
56236         
56237         var mstate = {};
56238         if (!this.ds.multiSort) { 
56239             var state = this.ds.getSortState();
56240             if(!state){
56241                 return;
56242             }
56243             mstate[state.field] = state.direction;
56244             // FIXME... - this is not used here.. but might be elsewhere..
56245             this.sortState = state;
56246             
56247         } else {
56248             mstate = this.ds.sortToggle;
56249         }
56250         //remove existing sort classes..
56251         
56252         var sc = this.sortClasses;
56253         var hds = this.el.select(this.headerSelector).removeClass(sc);
56254         
56255         for(var f in mstate) {
56256         
56257             var sortColumn = this.cm.findColumnIndex(f);
56258             
56259             if(sortColumn != -1){
56260                 var sortDir = mstate[f];        
56261                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
56262             }
56263         }
56264         
56265          
56266         
56267     },
56268
56269
56270     handleHeaderClick : function(g, index,e){
56271         
56272         Roo.log("header click");
56273         
56274         if (Roo.isTouch) {
56275             // touch events on header are handled by context
56276             this.handleHdCtx(g,index,e);
56277             return;
56278         }
56279         
56280         
56281         if(this.headersDisabled){
56282             return;
56283         }
56284         var dm = g.dataSource, cm = g.colModel;
56285         if(!cm.isSortable(index)){
56286             return;
56287         }
56288         g.stopEditing();
56289         
56290         if (dm.multiSort) {
56291             // update the sortOrder
56292             var so = [];
56293             for(var i = 0; i < cm.config.length; i++ ) {
56294                 
56295                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
56296                     continue; // dont' bother, it's not in sort list or being set.
56297                 }
56298                 
56299                 so.push(cm.config[i].dataIndex);
56300             };
56301             dm.sortOrder = so;
56302         }
56303         
56304         
56305         dm.sort(cm.getDataIndex(index));
56306     },
56307
56308
56309     destroy : function(){
56310         if(this.colMenu){
56311             this.colMenu.removeAll();
56312             Roo.menu.MenuMgr.unregister(this.colMenu);
56313             this.colMenu.getEl().remove();
56314             delete this.colMenu;
56315         }
56316         if(this.hmenu){
56317             this.hmenu.removeAll();
56318             Roo.menu.MenuMgr.unregister(this.hmenu);
56319             this.hmenu.getEl().remove();
56320             delete this.hmenu;
56321         }
56322         if(this.grid.enableColumnMove){
56323             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56324             if(dds){
56325                 for(var dd in dds){
56326                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
56327                         var elid = dds[dd].dragElId;
56328                         dds[dd].unreg();
56329                         Roo.get(elid).remove();
56330                     } else if(dds[dd].config.isTarget){
56331                         dds[dd].proxyTop.remove();
56332                         dds[dd].proxyBottom.remove();
56333                         dds[dd].unreg();
56334                     }
56335                     if(Roo.dd.DDM.locationCache[dd]){
56336                         delete Roo.dd.DDM.locationCache[dd];
56337                     }
56338                 }
56339                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56340             }
56341         }
56342         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
56343         this.bind(null, null);
56344         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
56345     },
56346
56347     handleLockChange : function(){
56348         this.refresh(true);
56349     },
56350
56351     onDenyColumnLock : function(){
56352
56353     },
56354
56355     onDenyColumnHide : function(){
56356
56357     },
56358
56359     handleHdMenuClick : function(item){
56360         var index = this.hdCtxIndex;
56361         var cm = this.cm, ds = this.ds;
56362         switch(item.id){
56363             case "asc":
56364                 ds.sort(cm.getDataIndex(index), "ASC");
56365                 break;
56366             case "desc":
56367                 ds.sort(cm.getDataIndex(index), "DESC");
56368                 break;
56369             case "lock":
56370                 var lc = cm.getLockedCount();
56371                 if(cm.getColumnCount(true) <= lc+1){
56372                     this.onDenyColumnLock();
56373                     return;
56374                 }
56375                 if(lc != index){
56376                     cm.setLocked(index, true, true);
56377                     cm.moveColumn(index, lc);
56378                     this.grid.fireEvent("columnmove", index, lc);
56379                 }else{
56380                     cm.setLocked(index, true);
56381                 }
56382             break;
56383             case "unlock":
56384                 var lc = cm.getLockedCount();
56385                 if((lc-1) != index){
56386                     cm.setLocked(index, false, true);
56387                     cm.moveColumn(index, lc-1);
56388                     this.grid.fireEvent("columnmove", index, lc-1);
56389                 }else{
56390                     cm.setLocked(index, false);
56391                 }
56392             break;
56393             case 'wider': // used to expand cols on touch..
56394             case 'narrow':
56395                 var cw = cm.getColumnWidth(index);
56396                 cw += (item.id == 'wider' ? 1 : -1) * 50;
56397                 cw = Math.max(0, cw);
56398                 cw = Math.min(cw,4000);
56399                 cm.setColumnWidth(index, cw);
56400                 break;
56401                 
56402             default:
56403                 index = cm.getIndexById(item.id.substr(4));
56404                 if(index != -1){
56405                     if(item.checked && cm.getColumnCount(true) <= 1){
56406                         this.onDenyColumnHide();
56407                         return false;
56408                     }
56409                     cm.setHidden(index, item.checked);
56410                 }
56411         }
56412         return true;
56413     },
56414
56415     beforeColMenuShow : function(){
56416         var cm = this.cm,  colCount = cm.getColumnCount();
56417         this.colMenu.removeAll();
56418         for(var i = 0; i < colCount; i++){
56419             this.colMenu.add(new Roo.menu.CheckItem({
56420                 id: "col-"+cm.getColumnId(i),
56421                 text: cm.getColumnHeader(i),
56422                 checked: !cm.isHidden(i),
56423                 hideOnClick:false
56424             }));
56425         }
56426     },
56427
56428     handleHdCtx : function(g, index, e){
56429         e.stopEvent();
56430         var hd = this.getHeaderCell(index);
56431         this.hdCtxIndex = index;
56432         var ms = this.hmenu.items, cm = this.cm;
56433         ms.get("asc").setDisabled(!cm.isSortable(index));
56434         ms.get("desc").setDisabled(!cm.isSortable(index));
56435         if(this.grid.enableColLock !== false){
56436             ms.get("lock").setDisabled(cm.isLocked(index));
56437             ms.get("unlock").setDisabled(!cm.isLocked(index));
56438         }
56439         this.hmenu.show(hd, "tl-bl");
56440     },
56441
56442     handleHdOver : function(e){
56443         var hd = this.findHeaderCell(e.getTarget());
56444         if(hd && !this.headersDisabled){
56445             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
56446                this.fly(hd).addClass("x-grid-hd-over");
56447             }
56448         }
56449     },
56450
56451     handleHdOut : function(e){
56452         var hd = this.findHeaderCell(e.getTarget());
56453         if(hd){
56454             this.fly(hd).removeClass("x-grid-hd-over");
56455         }
56456     },
56457
56458     handleSplitDblClick : function(e, t){
56459         var i = this.getCellIndex(t);
56460         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
56461             this.autoSizeColumn(i, true);
56462             this.layout();
56463         }
56464     },
56465
56466     render : function(){
56467
56468         var cm = this.cm;
56469         var colCount = cm.getColumnCount();
56470
56471         if(this.grid.monitorWindowResize === true){
56472             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
56473         }
56474         var header = this.renderHeaders();
56475         var body = this.templates.body.apply({rows:""});
56476         var html = this.templates.master.apply({
56477             lockedBody: body,
56478             body: body,
56479             lockedHeader: header[0],
56480             header: header[1]
56481         });
56482
56483         //this.updateColumns();
56484
56485         this.grid.getGridEl().dom.innerHTML = html;
56486
56487         this.initElements();
56488         
56489         // a kludge to fix the random scolling effect in webkit
56490         this.el.on("scroll", function() {
56491             this.el.dom.scrollTop=0; // hopefully not recursive..
56492         },this);
56493
56494         this.scroller.on("scroll", this.handleScroll, this);
56495         this.lockedBody.on("mousewheel", this.handleWheel, this);
56496         this.mainBody.on("mousewheel", this.handleWheel, this);
56497
56498         this.mainHd.on("mouseover", this.handleHdOver, this);
56499         this.mainHd.on("mouseout", this.handleHdOut, this);
56500         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
56501                 {delegate: "."+this.splitClass});
56502
56503         this.lockedHd.on("mouseover", this.handleHdOver, this);
56504         this.lockedHd.on("mouseout", this.handleHdOut, this);
56505         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
56506                 {delegate: "."+this.splitClass});
56507
56508         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
56509             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56510         }
56511
56512         this.updateSplitters();
56513
56514         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
56515             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56516             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56517         }
56518
56519         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
56520             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
56521             this.hmenu.add(
56522                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
56523                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
56524             );
56525             if(this.grid.enableColLock !== false){
56526                 this.hmenu.add('-',
56527                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
56528                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
56529                 );
56530             }
56531             if (Roo.isTouch) {
56532                  this.hmenu.add('-',
56533                     {id:"wider", text: this.columnsWiderText},
56534                     {id:"narrow", text: this.columnsNarrowText }
56535                 );
56536                 
56537                  
56538             }
56539             
56540             if(this.grid.enableColumnHide !== false){
56541
56542                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
56543                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
56544                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
56545
56546                 this.hmenu.add('-',
56547                     {id:"columns", text: this.columnsText, menu: this.colMenu}
56548                 );
56549             }
56550             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
56551
56552             this.grid.on("headercontextmenu", this.handleHdCtx, this);
56553         }
56554
56555         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
56556             this.dd = new Roo.grid.GridDragZone(this.grid, {
56557                 ddGroup : this.grid.ddGroup || 'GridDD'
56558             });
56559             
56560         }
56561
56562         /*
56563         for(var i = 0; i < colCount; i++){
56564             if(cm.isHidden(i)){
56565                 this.hideColumn(i);
56566             }
56567             if(cm.config[i].align){
56568                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
56569                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
56570             }
56571         }*/
56572         
56573         this.updateHeaderSortState();
56574
56575         this.beforeInitialResize();
56576         this.layout(true);
56577
56578         // two part rendering gives faster view to the user
56579         this.renderPhase2.defer(1, this);
56580     },
56581
56582     renderPhase2 : function(){
56583         // render the rows now
56584         this.refresh();
56585         if(this.grid.autoSizeColumns){
56586             this.autoSizeColumns();
56587         }
56588     },
56589
56590     beforeInitialResize : function(){
56591
56592     },
56593
56594     onColumnSplitterMoved : function(i, w){
56595         this.userResized = true;
56596         var cm = this.grid.colModel;
56597         cm.setColumnWidth(i, w, true);
56598         var cid = cm.getColumnId(i);
56599         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56600         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56601         this.updateSplitters();
56602         this.layout();
56603         this.grid.fireEvent("columnresize", i, w);
56604     },
56605
56606     syncRowHeights : function(startIndex, endIndex){
56607         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
56608             startIndex = startIndex || 0;
56609             var mrows = this.getBodyTable().rows;
56610             var lrows = this.getLockedTable().rows;
56611             var len = mrows.length-1;
56612             endIndex = Math.min(endIndex || len, len);
56613             for(var i = startIndex; i <= endIndex; i++){
56614                 var m = mrows[i], l = lrows[i];
56615                 var h = Math.max(m.offsetHeight, l.offsetHeight);
56616                 m.style.height = l.style.height = h + "px";
56617             }
56618         }
56619     },
56620
56621     layout : function(initialRender, is2ndPass){
56622         var g = this.grid;
56623         var auto = g.autoHeight;
56624         var scrollOffset = 16;
56625         var c = g.getGridEl(), cm = this.cm,
56626                 expandCol = g.autoExpandColumn,
56627                 gv = this;
56628         //c.beginMeasure();
56629
56630         if(!c.dom.offsetWidth){ // display:none?
56631             if(initialRender){
56632                 this.lockedWrap.show();
56633                 this.mainWrap.show();
56634             }
56635             return;
56636         }
56637
56638         var hasLock = this.cm.isLocked(0);
56639
56640         var tbh = this.headerPanel.getHeight();
56641         var bbh = this.footerPanel.getHeight();
56642
56643         if(auto){
56644             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
56645             var newHeight = ch + c.getBorderWidth("tb");
56646             if(g.maxHeight){
56647                 newHeight = Math.min(g.maxHeight, newHeight);
56648             }
56649             c.setHeight(newHeight);
56650         }
56651
56652         if(g.autoWidth){
56653             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
56654         }
56655
56656         var s = this.scroller;
56657
56658         var csize = c.getSize(true);
56659
56660         this.el.setSize(csize.width, csize.height);
56661
56662         this.headerPanel.setWidth(csize.width);
56663         this.footerPanel.setWidth(csize.width);
56664
56665         var hdHeight = this.mainHd.getHeight();
56666         var vw = csize.width;
56667         var vh = csize.height - (tbh + bbh);
56668
56669         s.setSize(vw, vh);
56670
56671         var bt = this.getBodyTable();
56672         
56673         if(cm.getLockedCount() == cm.config.length){
56674             bt = this.getLockedTable();
56675         }
56676         
56677         var ltWidth = hasLock ?
56678                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
56679
56680         var scrollHeight = bt.offsetHeight;
56681         var scrollWidth = ltWidth + bt.offsetWidth;
56682         var vscroll = false, hscroll = false;
56683
56684         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
56685
56686         var lw = this.lockedWrap, mw = this.mainWrap;
56687         var lb = this.lockedBody, mb = this.mainBody;
56688
56689         setTimeout(function(){
56690             var t = s.dom.offsetTop;
56691             var w = s.dom.clientWidth,
56692                 h = s.dom.clientHeight;
56693
56694             lw.setTop(t);
56695             lw.setSize(ltWidth, h);
56696
56697             mw.setLeftTop(ltWidth, t);
56698             mw.setSize(w-ltWidth, h);
56699
56700             lb.setHeight(h-hdHeight);
56701             mb.setHeight(h-hdHeight);
56702
56703             if(is2ndPass !== true && !gv.userResized && expandCol){
56704                 // high speed resize without full column calculation
56705                 
56706                 var ci = cm.getIndexById(expandCol);
56707                 if (ci < 0) {
56708                     ci = cm.findColumnIndex(expandCol);
56709                 }
56710                 ci = Math.max(0, ci); // make sure it's got at least the first col.
56711                 var expandId = cm.getColumnId(ci);
56712                 var  tw = cm.getTotalWidth(false);
56713                 var currentWidth = cm.getColumnWidth(ci);
56714                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
56715                 if(currentWidth != cw){
56716                     cm.setColumnWidth(ci, cw, true);
56717                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56718                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56719                     gv.updateSplitters();
56720                     gv.layout(false, true);
56721                 }
56722             }
56723
56724             if(initialRender){
56725                 lw.show();
56726                 mw.show();
56727             }
56728             //c.endMeasure();
56729         }, 10);
56730     },
56731
56732     onWindowResize : function(){
56733         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
56734             return;
56735         }
56736         this.layout();
56737     },
56738
56739     appendFooter : function(parentEl){
56740         return null;
56741     },
56742
56743     sortAscText : "Sort Ascending",
56744     sortDescText : "Sort Descending",
56745     lockText : "Lock Column",
56746     unlockText : "Unlock Column",
56747     columnsText : "Columns",
56748  
56749     columnsWiderText : "Wider",
56750     columnsNarrowText : "Thinner"
56751 });
56752
56753
56754 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
56755     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
56756     this.proxy.el.addClass('x-grid3-col-dd');
56757 };
56758
56759 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
56760     handleMouseDown : function(e){
56761
56762     },
56763
56764     callHandleMouseDown : function(e){
56765         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
56766     }
56767 });
56768 /*
56769  * Based on:
56770  * Ext JS Library 1.1.1
56771  * Copyright(c) 2006-2007, Ext JS, LLC.
56772  *
56773  * Originally Released Under LGPL - original licence link has changed is not relivant.
56774  *
56775  * Fork - LGPL
56776  * <script type="text/javascript">
56777  */
56778  
56779 // private
56780 // This is a support class used internally by the Grid components
56781 Roo.grid.SplitDragZone = function(grid, hd, hd2){
56782     this.grid = grid;
56783     this.view = grid.getView();
56784     this.proxy = this.view.resizeProxy;
56785     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
56786         "gridSplitters" + this.grid.getGridEl().id, {
56787         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
56788     });
56789     this.setHandleElId(Roo.id(hd));
56790     this.setOuterHandleElId(Roo.id(hd2));
56791     this.scroll = false;
56792 };
56793 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
56794     fly: Roo.Element.fly,
56795
56796     b4StartDrag : function(x, y){
56797         this.view.headersDisabled = true;
56798         this.proxy.setHeight(this.view.mainWrap.getHeight());
56799         var w = this.cm.getColumnWidth(this.cellIndex);
56800         var minw = Math.max(w-this.grid.minColumnWidth, 0);
56801         this.resetConstraints();
56802         this.setXConstraint(minw, 1000);
56803         this.setYConstraint(0, 0);
56804         this.minX = x - minw;
56805         this.maxX = x + 1000;
56806         this.startPos = x;
56807         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
56808     },
56809
56810
56811     handleMouseDown : function(e){
56812         ev = Roo.EventObject.setEvent(e);
56813         var t = this.fly(ev.getTarget());
56814         if(t.hasClass("x-grid-split")){
56815             this.cellIndex = this.view.getCellIndex(t.dom);
56816             this.split = t.dom;
56817             this.cm = this.grid.colModel;
56818             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
56819                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
56820             }
56821         }
56822     },
56823
56824     endDrag : function(e){
56825         this.view.headersDisabled = false;
56826         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
56827         var diff = endX - this.startPos;
56828         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
56829     },
56830
56831     autoOffset : function(){
56832         this.setDelta(0,0);
56833     }
56834 });/*
56835  * Based on:
56836  * Ext JS Library 1.1.1
56837  * Copyright(c) 2006-2007, Ext JS, LLC.
56838  *
56839  * Originally Released Under LGPL - original licence link has changed is not relivant.
56840  *
56841  * Fork - LGPL
56842  * <script type="text/javascript">
56843  */
56844  
56845 // private
56846 // This is a support class used internally by the Grid components
56847 Roo.grid.GridDragZone = function(grid, config){
56848     this.view = grid.getView();
56849     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
56850     if(this.view.lockedBody){
56851         this.setHandleElId(Roo.id(this.view.mainBody.dom));
56852         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
56853     }
56854     this.scroll = false;
56855     this.grid = grid;
56856     this.ddel = document.createElement('div');
56857     this.ddel.className = 'x-grid-dd-wrap';
56858 };
56859
56860 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
56861     ddGroup : "GridDD",
56862
56863     getDragData : function(e){
56864         var t = Roo.lib.Event.getTarget(e);
56865         var rowIndex = this.view.findRowIndex(t);
56866         var sm = this.grid.selModel;
56867             
56868         //Roo.log(rowIndex);
56869         
56870         if (sm.getSelectedCell) {
56871             // cell selection..
56872             if (!sm.getSelectedCell()) {
56873                 return false;
56874             }
56875             if (rowIndex != sm.getSelectedCell()[0]) {
56876                 return false;
56877             }
56878         
56879         }
56880         
56881         if(rowIndex !== false){
56882             
56883             // if editorgrid.. 
56884             
56885             
56886             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
56887                
56888             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
56889               //  
56890             //}
56891             if (e.hasModifier()){
56892                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
56893             }
56894             
56895             Roo.log("getDragData");
56896             
56897             return {
56898                 grid: this.grid,
56899                 ddel: this.ddel,
56900                 rowIndex: rowIndex,
56901                 selections:sm.getSelections ? sm.getSelections() : (
56902                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
56903                 )
56904             };
56905         }
56906         return false;
56907     },
56908
56909     onInitDrag : function(e){
56910         var data = this.dragData;
56911         this.ddel.innerHTML = this.grid.getDragDropText();
56912         this.proxy.update(this.ddel);
56913         // fire start drag?
56914     },
56915
56916     afterRepair : function(){
56917         this.dragging = false;
56918     },
56919
56920     getRepairXY : function(e, data){
56921         return false;
56922     },
56923
56924     onEndDrag : function(data, e){
56925         // fire end drag?
56926     },
56927
56928     onValidDrop : function(dd, e, id){
56929         // fire drag drop?
56930         this.hideProxy();
56931     },
56932
56933     beforeInvalidDrop : function(e, id){
56934
56935     }
56936 });/*
56937  * Based on:
56938  * Ext JS Library 1.1.1
56939  * Copyright(c) 2006-2007, Ext JS, LLC.
56940  *
56941  * Originally Released Under LGPL - original licence link has changed is not relivant.
56942  *
56943  * Fork - LGPL
56944  * <script type="text/javascript">
56945  */
56946  
56947
56948 /**
56949  * @class Roo.grid.ColumnModel
56950  * @extends Roo.util.Observable
56951  * This is the default implementation of a ColumnModel used by the Grid. It defines
56952  * the columns in the grid.
56953  * <br>Usage:<br>
56954  <pre><code>
56955  var colModel = new Roo.grid.ColumnModel([
56956         {header: "Ticker", width: 60, sortable: true, locked: true},
56957         {header: "Company Name", width: 150, sortable: true},
56958         {header: "Market Cap.", width: 100, sortable: true},
56959         {header: "$ Sales", width: 100, sortable: true, renderer: money},
56960         {header: "Employees", width: 100, sortable: true, resizable: false}
56961  ]);
56962  </code></pre>
56963  * <p>
56964  
56965  * The config options listed for this class are options which may appear in each
56966  * individual column definition.
56967  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
56968  * @constructor
56969  * @param {Object} config An Array of column config objects. See this class's
56970  * config objects for details.
56971 */
56972 Roo.grid.ColumnModel = function(config){
56973         /**
56974      * The config passed into the constructor
56975      */
56976     this.config = config;
56977     this.lookup = {};
56978
56979     // if no id, create one
56980     // if the column does not have a dataIndex mapping,
56981     // map it to the order it is in the config
56982     for(var i = 0, len = config.length; i < len; i++){
56983         var c = config[i];
56984         if(typeof c.dataIndex == "undefined"){
56985             c.dataIndex = i;
56986         }
56987         if(typeof c.renderer == "string"){
56988             c.renderer = Roo.util.Format[c.renderer];
56989         }
56990         if(typeof c.id == "undefined"){
56991             c.id = Roo.id();
56992         }
56993         if(c.editor && c.editor.xtype){
56994             c.editor  = Roo.factory(c.editor, Roo.grid);
56995         }
56996         if(c.editor && c.editor.isFormField){
56997             c.editor = new Roo.grid.GridEditor(c.editor);
56998         }
56999         this.lookup[c.id] = c;
57000     }
57001
57002     /**
57003      * The width of columns which have no width specified (defaults to 100)
57004      * @type Number
57005      */
57006     this.defaultWidth = 100;
57007
57008     /**
57009      * Default sortable of columns which have no sortable specified (defaults to false)
57010      * @type Boolean
57011      */
57012     this.defaultSortable = false;
57013
57014     this.addEvents({
57015         /**
57016              * @event widthchange
57017              * Fires when the width of a column changes.
57018              * @param {ColumnModel} this
57019              * @param {Number} columnIndex The column index
57020              * @param {Number} newWidth The new width
57021              */
57022             "widthchange": true,
57023         /**
57024              * @event headerchange
57025              * Fires when the text of a header changes.
57026              * @param {ColumnModel} this
57027              * @param {Number} columnIndex The column index
57028              * @param {Number} newText The new header text
57029              */
57030             "headerchange": true,
57031         /**
57032              * @event hiddenchange
57033              * Fires when a column is hidden or "unhidden".
57034              * @param {ColumnModel} this
57035              * @param {Number} columnIndex The column index
57036              * @param {Boolean} hidden true if hidden, false otherwise
57037              */
57038             "hiddenchange": true,
57039             /**
57040          * @event columnmoved
57041          * Fires when a column is moved.
57042          * @param {ColumnModel} this
57043          * @param {Number} oldIndex
57044          * @param {Number} newIndex
57045          */
57046         "columnmoved" : true,
57047         /**
57048          * @event columlockchange
57049          * Fires when a column's locked state is changed
57050          * @param {ColumnModel} this
57051          * @param {Number} colIndex
57052          * @param {Boolean} locked true if locked
57053          */
57054         "columnlockchange" : true
57055     });
57056     Roo.grid.ColumnModel.superclass.constructor.call(this);
57057 };
57058 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
57059     /**
57060      * @cfg {String} header The header text to display in the Grid view.
57061      */
57062     /**
57063      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
57064      * {@link Roo.data.Record} definition from which to draw the column's value. If not
57065      * specified, the column's index is used as an index into the Record's data Array.
57066      */
57067     /**
57068      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
57069      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
57070      */
57071     /**
57072      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
57073      * Defaults to the value of the {@link #defaultSortable} property.
57074      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
57075      */
57076     /**
57077      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
57078      */
57079     /**
57080      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
57081      */
57082     /**
57083      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
57084      */
57085     /**
57086      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
57087      */
57088     /**
57089      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
57090      * given the cell's data value. See {@link #setRenderer}. If not specified, the
57091      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
57092      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
57093      */
57094        /**
57095      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
57096      */
57097     /**
57098      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
57099      */
57100     /**
57101      * @cfg {String} cursor (Optional)
57102      */
57103     /**
57104      * @cfg {String} tooltip (Optional)
57105      */
57106     /**
57107      * @cfg {Number} xs (Optional)
57108      */
57109     /**
57110      * @cfg {Number} sm (Optional)
57111      */
57112     /**
57113      * @cfg {Number} md (Optional)
57114      */
57115     /**
57116      * @cfg {Number} lg (Optional)
57117      */
57118     /**
57119      * Returns the id of the column at the specified index.
57120      * @param {Number} index The column index
57121      * @return {String} the id
57122      */
57123     getColumnId : function(index){
57124         return this.config[index].id;
57125     },
57126
57127     /**
57128      * Returns the column for a specified id.
57129      * @param {String} id The column id
57130      * @return {Object} the column
57131      */
57132     getColumnById : function(id){
57133         return this.lookup[id];
57134     },
57135
57136     
57137     /**
57138      * Returns the column for a specified dataIndex.
57139      * @param {String} dataIndex The column dataIndex
57140      * @return {Object|Boolean} the column or false if not found
57141      */
57142     getColumnByDataIndex: function(dataIndex){
57143         var index = this.findColumnIndex(dataIndex);
57144         return index > -1 ? this.config[index] : false;
57145     },
57146     
57147     /**
57148      * Returns the index for a specified column id.
57149      * @param {String} id The column id
57150      * @return {Number} the index, or -1 if not found
57151      */
57152     getIndexById : function(id){
57153         for(var i = 0, len = this.config.length; i < len; i++){
57154             if(this.config[i].id == id){
57155                 return i;
57156             }
57157         }
57158         return -1;
57159     },
57160     
57161     /**
57162      * Returns the index for a specified column dataIndex.
57163      * @param {String} dataIndex The column dataIndex
57164      * @return {Number} the index, or -1 if not found
57165      */
57166     
57167     findColumnIndex : function(dataIndex){
57168         for(var i = 0, len = this.config.length; i < len; i++){
57169             if(this.config[i].dataIndex == dataIndex){
57170                 return i;
57171             }
57172         }
57173         return -1;
57174     },
57175     
57176     
57177     moveColumn : function(oldIndex, newIndex){
57178         var c = this.config[oldIndex];
57179         this.config.splice(oldIndex, 1);
57180         this.config.splice(newIndex, 0, c);
57181         this.dataMap = null;
57182         this.fireEvent("columnmoved", this, oldIndex, newIndex);
57183     },
57184
57185     isLocked : function(colIndex){
57186         return this.config[colIndex].locked === true;
57187     },
57188
57189     setLocked : function(colIndex, value, suppressEvent){
57190         if(this.isLocked(colIndex) == value){
57191             return;
57192         }
57193         this.config[colIndex].locked = value;
57194         if(!suppressEvent){
57195             this.fireEvent("columnlockchange", this, colIndex, value);
57196         }
57197     },
57198
57199     getTotalLockedWidth : function(){
57200         var totalWidth = 0;
57201         for(var i = 0; i < this.config.length; i++){
57202             if(this.isLocked(i) && !this.isHidden(i)){
57203                 this.totalWidth += this.getColumnWidth(i);
57204             }
57205         }
57206         return totalWidth;
57207     },
57208
57209     getLockedCount : function(){
57210         for(var i = 0, len = this.config.length; i < len; i++){
57211             if(!this.isLocked(i)){
57212                 return i;
57213             }
57214         }
57215         
57216         return this.config.length;
57217     },
57218
57219     /**
57220      * Returns the number of columns.
57221      * @return {Number}
57222      */
57223     getColumnCount : function(visibleOnly){
57224         if(visibleOnly === true){
57225             var c = 0;
57226             for(var i = 0, len = this.config.length; i < len; i++){
57227                 if(!this.isHidden(i)){
57228                     c++;
57229                 }
57230             }
57231             return c;
57232         }
57233         return this.config.length;
57234     },
57235
57236     /**
57237      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
57238      * @param {Function} fn
57239      * @param {Object} scope (optional)
57240      * @return {Array} result
57241      */
57242     getColumnsBy : function(fn, scope){
57243         var r = [];
57244         for(var i = 0, len = this.config.length; i < len; i++){
57245             var c = this.config[i];
57246             if(fn.call(scope||this, c, i) === true){
57247                 r[r.length] = c;
57248             }
57249         }
57250         return r;
57251     },
57252
57253     /**
57254      * Returns true if the specified column is sortable.
57255      * @param {Number} col The column index
57256      * @return {Boolean}
57257      */
57258     isSortable : function(col){
57259         if(typeof this.config[col].sortable == "undefined"){
57260             return this.defaultSortable;
57261         }
57262         return this.config[col].sortable;
57263     },
57264
57265     /**
57266      * Returns the rendering (formatting) function defined for the column.
57267      * @param {Number} col The column index.
57268      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
57269      */
57270     getRenderer : function(col){
57271         if(!this.config[col].renderer){
57272             return Roo.grid.ColumnModel.defaultRenderer;
57273         }
57274         return this.config[col].renderer;
57275     },
57276
57277     /**
57278      * Sets the rendering (formatting) function for a column.
57279      * @param {Number} col The column index
57280      * @param {Function} fn The function to use to process the cell's raw data
57281      * to return HTML markup for the grid view. The render function is called with
57282      * the following parameters:<ul>
57283      * <li>Data value.</li>
57284      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
57285      * <li>css A CSS style string to apply to the table cell.</li>
57286      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
57287      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
57288      * <li>Row index</li>
57289      * <li>Column index</li>
57290      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
57291      */
57292     setRenderer : function(col, fn){
57293         this.config[col].renderer = fn;
57294     },
57295
57296     /**
57297      * Returns the width for the specified column.
57298      * @param {Number} col The column index
57299      * @return {Number}
57300      */
57301     getColumnWidth : function(col){
57302         return this.config[col].width * 1 || this.defaultWidth;
57303     },
57304
57305     /**
57306      * Sets the width for a column.
57307      * @param {Number} col The column index
57308      * @param {Number} width The new width
57309      */
57310     setColumnWidth : function(col, width, suppressEvent){
57311         this.config[col].width = width;
57312         this.totalWidth = null;
57313         if(!suppressEvent){
57314              this.fireEvent("widthchange", this, col, width);
57315         }
57316     },
57317
57318     /**
57319      * Returns the total width of all columns.
57320      * @param {Boolean} includeHidden True to include hidden column widths
57321      * @return {Number}
57322      */
57323     getTotalWidth : function(includeHidden){
57324         if(!this.totalWidth){
57325             this.totalWidth = 0;
57326             for(var i = 0, len = this.config.length; i < len; i++){
57327                 if(includeHidden || !this.isHidden(i)){
57328                     this.totalWidth += this.getColumnWidth(i);
57329                 }
57330             }
57331         }
57332         return this.totalWidth;
57333     },
57334
57335     /**
57336      * Returns the header for the specified column.
57337      * @param {Number} col The column index
57338      * @return {String}
57339      */
57340     getColumnHeader : function(col){
57341         return this.config[col].header;
57342     },
57343
57344     /**
57345      * Sets the header for a column.
57346      * @param {Number} col The column index
57347      * @param {String} header The new header
57348      */
57349     setColumnHeader : function(col, header){
57350         this.config[col].header = header;
57351         this.fireEvent("headerchange", this, col, header);
57352     },
57353
57354     /**
57355      * Returns the tooltip for the specified column.
57356      * @param {Number} col The column index
57357      * @return {String}
57358      */
57359     getColumnTooltip : function(col){
57360             return this.config[col].tooltip;
57361     },
57362     /**
57363      * Sets the tooltip for a column.
57364      * @param {Number} col The column index
57365      * @param {String} tooltip The new tooltip
57366      */
57367     setColumnTooltip : function(col, tooltip){
57368             this.config[col].tooltip = tooltip;
57369     },
57370
57371     /**
57372      * Returns the dataIndex for the specified column.
57373      * @param {Number} col The column index
57374      * @return {Number}
57375      */
57376     getDataIndex : function(col){
57377         return this.config[col].dataIndex;
57378     },
57379
57380     /**
57381      * Sets the dataIndex for a column.
57382      * @param {Number} col The column index
57383      * @param {Number} dataIndex The new dataIndex
57384      */
57385     setDataIndex : function(col, dataIndex){
57386         this.config[col].dataIndex = dataIndex;
57387     },
57388
57389     
57390     
57391     /**
57392      * Returns true if the cell is editable.
57393      * @param {Number} colIndex The column index
57394      * @param {Number} rowIndex The row index - this is nto actually used..?
57395      * @return {Boolean}
57396      */
57397     isCellEditable : function(colIndex, rowIndex){
57398         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
57399     },
57400
57401     /**
57402      * Returns the editor defined for the cell/column.
57403      * return false or null to disable editing.
57404      * @param {Number} colIndex The column index
57405      * @param {Number} rowIndex The row index
57406      * @return {Object}
57407      */
57408     getCellEditor : function(colIndex, rowIndex){
57409         return this.config[colIndex].editor;
57410     },
57411
57412     /**
57413      * Sets if a column is editable.
57414      * @param {Number} col The column index
57415      * @param {Boolean} editable True if the column is editable
57416      */
57417     setEditable : function(col, editable){
57418         this.config[col].editable = editable;
57419     },
57420
57421
57422     /**
57423      * Returns true if the column is hidden.
57424      * @param {Number} colIndex The column index
57425      * @return {Boolean}
57426      */
57427     isHidden : function(colIndex){
57428         return this.config[colIndex].hidden;
57429     },
57430
57431
57432     /**
57433      * Returns true if the column width cannot be changed
57434      */
57435     isFixed : function(colIndex){
57436         return this.config[colIndex].fixed;
57437     },
57438
57439     /**
57440      * Returns true if the column can be resized
57441      * @return {Boolean}
57442      */
57443     isResizable : function(colIndex){
57444         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
57445     },
57446     /**
57447      * Sets if a column is hidden.
57448      * @param {Number} colIndex The column index
57449      * @param {Boolean} hidden True if the column is hidden
57450      */
57451     setHidden : function(colIndex, hidden){
57452         this.config[colIndex].hidden = hidden;
57453         this.totalWidth = null;
57454         this.fireEvent("hiddenchange", this, colIndex, hidden);
57455     },
57456
57457     /**
57458      * Sets the editor for a column.
57459      * @param {Number} col The column index
57460      * @param {Object} editor The editor object
57461      */
57462     setEditor : function(col, editor){
57463         this.config[col].editor = editor;
57464     }
57465 });
57466
57467 Roo.grid.ColumnModel.defaultRenderer = function(value)
57468 {
57469     if(typeof value == "object") {
57470         return value;
57471     }
57472         if(typeof value == "string" && value.length < 1){
57473             return "&#160;";
57474         }
57475     
57476         return String.format("{0}", value);
57477 };
57478
57479 // Alias for backwards compatibility
57480 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
57481 /*
57482  * Based on:
57483  * Ext JS Library 1.1.1
57484  * Copyright(c) 2006-2007, Ext JS, LLC.
57485  *
57486  * Originally Released Under LGPL - original licence link has changed is not relivant.
57487  *
57488  * Fork - LGPL
57489  * <script type="text/javascript">
57490  */
57491
57492 /**
57493  * @class Roo.grid.AbstractSelectionModel
57494  * @extends Roo.util.Observable
57495  * Abstract base class for grid SelectionModels.  It provides the interface that should be
57496  * implemented by descendant classes.  This class should not be directly instantiated.
57497  * @constructor
57498  */
57499 Roo.grid.AbstractSelectionModel = function(){
57500     this.locked = false;
57501     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
57502 };
57503
57504 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
57505     /** @ignore Called by the grid automatically. Do not call directly. */
57506     init : function(grid){
57507         this.grid = grid;
57508         this.initEvents();
57509     },
57510
57511     /**
57512      * Locks the selections.
57513      */
57514     lock : function(){
57515         this.locked = true;
57516     },
57517
57518     /**
57519      * Unlocks the selections.
57520      */
57521     unlock : function(){
57522         this.locked = false;
57523     },
57524
57525     /**
57526      * Returns true if the selections are locked.
57527      * @return {Boolean}
57528      */
57529     isLocked : function(){
57530         return this.locked;
57531     }
57532 });/*
57533  * Based on:
57534  * Ext JS Library 1.1.1
57535  * Copyright(c) 2006-2007, Ext JS, LLC.
57536  *
57537  * Originally Released Under LGPL - original licence link has changed is not relivant.
57538  *
57539  * Fork - LGPL
57540  * <script type="text/javascript">
57541  */
57542 /**
57543  * @extends Roo.grid.AbstractSelectionModel
57544  * @class Roo.grid.RowSelectionModel
57545  * The default SelectionModel used by {@link Roo.grid.Grid}.
57546  * It supports multiple selections and keyboard selection/navigation. 
57547  * @constructor
57548  * @param {Object} config
57549  */
57550 Roo.grid.RowSelectionModel = function(config){
57551     Roo.apply(this, config);
57552     this.selections = new Roo.util.MixedCollection(false, function(o){
57553         return o.id;
57554     });
57555
57556     this.last = false;
57557     this.lastActive = false;
57558
57559     this.addEvents({
57560         /**
57561              * @event selectionchange
57562              * Fires when the selection changes
57563              * @param {SelectionModel} this
57564              */
57565             "selectionchange" : true,
57566         /**
57567              * @event afterselectionchange
57568              * Fires after the selection changes (eg. by key press or clicking)
57569              * @param {SelectionModel} this
57570              */
57571             "afterselectionchange" : true,
57572         /**
57573              * @event beforerowselect
57574              * Fires when a row is selected being selected, return false to cancel.
57575              * @param {SelectionModel} this
57576              * @param {Number} rowIndex The selected index
57577              * @param {Boolean} keepExisting False if other selections will be cleared
57578              */
57579             "beforerowselect" : true,
57580         /**
57581              * @event rowselect
57582              * Fires when a row is selected.
57583              * @param {SelectionModel} this
57584              * @param {Number} rowIndex The selected index
57585              * @param {Roo.data.Record} r The record
57586              */
57587             "rowselect" : true,
57588         /**
57589              * @event rowdeselect
57590              * Fires when a row is deselected.
57591              * @param {SelectionModel} this
57592              * @param {Number} rowIndex The selected index
57593              */
57594         "rowdeselect" : true
57595     });
57596     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
57597     this.locked = false;
57598 };
57599
57600 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
57601     /**
57602      * @cfg {Boolean} singleSelect
57603      * True to allow selection of only one row at a time (defaults to false)
57604      */
57605     singleSelect : false,
57606
57607     // private
57608     initEvents : function(){
57609
57610         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
57611             this.grid.on("mousedown", this.handleMouseDown, this);
57612         }else{ // allow click to work like normal
57613             this.grid.on("rowclick", this.handleDragableRowClick, this);
57614         }
57615
57616         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
57617             "up" : function(e){
57618                 if(!e.shiftKey){
57619                     this.selectPrevious(e.shiftKey);
57620                 }else if(this.last !== false && this.lastActive !== false){
57621                     var last = this.last;
57622                     this.selectRange(this.last,  this.lastActive-1);
57623                     this.grid.getView().focusRow(this.lastActive);
57624                     if(last !== false){
57625                         this.last = last;
57626                     }
57627                 }else{
57628                     this.selectFirstRow();
57629                 }
57630                 this.fireEvent("afterselectionchange", this);
57631             },
57632             "down" : function(e){
57633                 if(!e.shiftKey){
57634                     this.selectNext(e.shiftKey);
57635                 }else if(this.last !== false && this.lastActive !== false){
57636                     var last = this.last;
57637                     this.selectRange(this.last,  this.lastActive+1);
57638                     this.grid.getView().focusRow(this.lastActive);
57639                     if(last !== false){
57640                         this.last = last;
57641                     }
57642                 }else{
57643                     this.selectFirstRow();
57644                 }
57645                 this.fireEvent("afterselectionchange", this);
57646             },
57647             scope: this
57648         });
57649
57650         var view = this.grid.view;
57651         view.on("refresh", this.onRefresh, this);
57652         view.on("rowupdated", this.onRowUpdated, this);
57653         view.on("rowremoved", this.onRemove, this);
57654     },
57655
57656     // private
57657     onRefresh : function(){
57658         var ds = this.grid.dataSource, i, v = this.grid.view;
57659         var s = this.selections;
57660         s.each(function(r){
57661             if((i = ds.indexOfId(r.id)) != -1){
57662                 v.onRowSelect(i);
57663                 s.add(ds.getAt(i)); // updating the selection relate data
57664             }else{
57665                 s.remove(r);
57666             }
57667         });
57668     },
57669
57670     // private
57671     onRemove : function(v, index, r){
57672         this.selections.remove(r);
57673     },
57674
57675     // private
57676     onRowUpdated : function(v, index, r){
57677         if(this.isSelected(r)){
57678             v.onRowSelect(index);
57679         }
57680     },
57681
57682     /**
57683      * Select records.
57684      * @param {Array} records The records to select
57685      * @param {Boolean} keepExisting (optional) True to keep existing selections
57686      */
57687     selectRecords : function(records, keepExisting){
57688         if(!keepExisting){
57689             this.clearSelections();
57690         }
57691         var ds = this.grid.dataSource;
57692         for(var i = 0, len = records.length; i < len; i++){
57693             this.selectRow(ds.indexOf(records[i]), true);
57694         }
57695     },
57696
57697     /**
57698      * Gets the number of selected rows.
57699      * @return {Number}
57700      */
57701     getCount : function(){
57702         return this.selections.length;
57703     },
57704
57705     /**
57706      * Selects the first row in the grid.
57707      */
57708     selectFirstRow : function(){
57709         this.selectRow(0);
57710     },
57711
57712     /**
57713      * Select the last row.
57714      * @param {Boolean} keepExisting (optional) True to keep existing selections
57715      */
57716     selectLastRow : function(keepExisting){
57717         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
57718     },
57719
57720     /**
57721      * Selects the row immediately following the last selected row.
57722      * @param {Boolean} keepExisting (optional) True to keep existing selections
57723      */
57724     selectNext : function(keepExisting){
57725         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
57726             this.selectRow(this.last+1, keepExisting);
57727             this.grid.getView().focusRow(this.last);
57728         }
57729     },
57730
57731     /**
57732      * Selects the row that precedes the last selected row.
57733      * @param {Boolean} keepExisting (optional) True to keep existing selections
57734      */
57735     selectPrevious : function(keepExisting){
57736         if(this.last){
57737             this.selectRow(this.last-1, keepExisting);
57738             this.grid.getView().focusRow(this.last);
57739         }
57740     },
57741
57742     /**
57743      * Returns the selected records
57744      * @return {Array} Array of selected records
57745      */
57746     getSelections : function(){
57747         return [].concat(this.selections.items);
57748     },
57749
57750     /**
57751      * Returns the first selected record.
57752      * @return {Record}
57753      */
57754     getSelected : function(){
57755         return this.selections.itemAt(0);
57756     },
57757
57758
57759     /**
57760      * Clears all selections.
57761      */
57762     clearSelections : function(fast){
57763         if(this.locked) {
57764             return;
57765         }
57766         if(fast !== true){
57767             var ds = this.grid.dataSource;
57768             var s = this.selections;
57769             s.each(function(r){
57770                 this.deselectRow(ds.indexOfId(r.id));
57771             }, this);
57772             s.clear();
57773         }else{
57774             this.selections.clear();
57775         }
57776         this.last = false;
57777     },
57778
57779
57780     /**
57781      * Selects all rows.
57782      */
57783     selectAll : function(){
57784         if(this.locked) {
57785             return;
57786         }
57787         this.selections.clear();
57788         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
57789             this.selectRow(i, true);
57790         }
57791     },
57792
57793     /**
57794      * Returns True if there is a selection.
57795      * @return {Boolean}
57796      */
57797     hasSelection : function(){
57798         return this.selections.length > 0;
57799     },
57800
57801     /**
57802      * Returns True if the specified row is selected.
57803      * @param {Number/Record} record The record or index of the record to check
57804      * @return {Boolean}
57805      */
57806     isSelected : function(index){
57807         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
57808         return (r && this.selections.key(r.id) ? true : false);
57809     },
57810
57811     /**
57812      * Returns True if the specified record id is selected.
57813      * @param {String} id The id of record to check
57814      * @return {Boolean}
57815      */
57816     isIdSelected : function(id){
57817         return (this.selections.key(id) ? true : false);
57818     },
57819
57820     // private
57821     handleMouseDown : function(e, t){
57822         var view = this.grid.getView(), rowIndex;
57823         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
57824             return;
57825         };
57826         if(e.shiftKey && this.last !== false){
57827             var last = this.last;
57828             this.selectRange(last, rowIndex, e.ctrlKey);
57829             this.last = last; // reset the last
57830             view.focusRow(rowIndex);
57831         }else{
57832             var isSelected = this.isSelected(rowIndex);
57833             if(e.button !== 0 && isSelected){
57834                 view.focusRow(rowIndex);
57835             }else if(e.ctrlKey && isSelected){
57836                 this.deselectRow(rowIndex);
57837             }else if(!isSelected){
57838                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
57839                 view.focusRow(rowIndex);
57840             }
57841         }
57842         this.fireEvent("afterselectionchange", this);
57843     },
57844     // private
57845     handleDragableRowClick :  function(grid, rowIndex, e) 
57846     {
57847         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
57848             this.selectRow(rowIndex, false);
57849             grid.view.focusRow(rowIndex);
57850              this.fireEvent("afterselectionchange", this);
57851         }
57852     },
57853     
57854     /**
57855      * Selects multiple rows.
57856      * @param {Array} rows Array of the indexes of the row to select
57857      * @param {Boolean} keepExisting (optional) True to keep existing selections
57858      */
57859     selectRows : function(rows, keepExisting){
57860         if(!keepExisting){
57861             this.clearSelections();
57862         }
57863         for(var i = 0, len = rows.length; i < len; i++){
57864             this.selectRow(rows[i], true);
57865         }
57866     },
57867
57868     /**
57869      * Selects a range of rows. All rows in between startRow and endRow are also selected.
57870      * @param {Number} startRow The index of the first row in the range
57871      * @param {Number} endRow The index of the last row in the range
57872      * @param {Boolean} keepExisting (optional) True to retain existing selections
57873      */
57874     selectRange : function(startRow, endRow, keepExisting){
57875         if(this.locked) {
57876             return;
57877         }
57878         if(!keepExisting){
57879             this.clearSelections();
57880         }
57881         if(startRow <= endRow){
57882             for(var i = startRow; i <= endRow; i++){
57883                 this.selectRow(i, true);
57884             }
57885         }else{
57886             for(var i = startRow; i >= endRow; i--){
57887                 this.selectRow(i, true);
57888             }
57889         }
57890     },
57891
57892     /**
57893      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
57894      * @param {Number} startRow The index of the first row in the range
57895      * @param {Number} endRow The index of the last row in the range
57896      */
57897     deselectRange : function(startRow, endRow, preventViewNotify){
57898         if(this.locked) {
57899             return;
57900         }
57901         for(var i = startRow; i <= endRow; i++){
57902             this.deselectRow(i, preventViewNotify);
57903         }
57904     },
57905
57906     /**
57907      * Selects a row.
57908      * @param {Number} row The index of the row to select
57909      * @param {Boolean} keepExisting (optional) True to keep existing selections
57910      */
57911     selectRow : function(index, keepExisting, preventViewNotify){
57912         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
57913             return;
57914         }
57915         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
57916             if(!keepExisting || this.singleSelect){
57917                 this.clearSelections();
57918             }
57919             var r = this.grid.dataSource.getAt(index);
57920             this.selections.add(r);
57921             this.last = this.lastActive = index;
57922             if(!preventViewNotify){
57923                 this.grid.getView().onRowSelect(index);
57924             }
57925             this.fireEvent("rowselect", this, index, r);
57926             this.fireEvent("selectionchange", this);
57927         }
57928     },
57929
57930     /**
57931      * Deselects a row.
57932      * @param {Number} row The index of the row to deselect
57933      */
57934     deselectRow : function(index, preventViewNotify){
57935         if(this.locked) {
57936             return;
57937         }
57938         if(this.last == index){
57939             this.last = false;
57940         }
57941         if(this.lastActive == index){
57942             this.lastActive = false;
57943         }
57944         var r = this.grid.dataSource.getAt(index);
57945         this.selections.remove(r);
57946         if(!preventViewNotify){
57947             this.grid.getView().onRowDeselect(index);
57948         }
57949         this.fireEvent("rowdeselect", this, index);
57950         this.fireEvent("selectionchange", this);
57951     },
57952
57953     // private
57954     restoreLast : function(){
57955         if(this._last){
57956             this.last = this._last;
57957         }
57958     },
57959
57960     // private
57961     acceptsNav : function(row, col, cm){
57962         return !cm.isHidden(col) && cm.isCellEditable(col, row);
57963     },
57964
57965     // private
57966     onEditorKey : function(field, e){
57967         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
57968         if(k == e.TAB){
57969             e.stopEvent();
57970             ed.completeEdit();
57971             if(e.shiftKey){
57972                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
57973             }else{
57974                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
57975             }
57976         }else if(k == e.ENTER && !e.ctrlKey){
57977             e.stopEvent();
57978             ed.completeEdit();
57979             if(e.shiftKey){
57980                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
57981             }else{
57982                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
57983             }
57984         }else if(k == e.ESC){
57985             ed.cancelEdit();
57986         }
57987         if(newCell){
57988             g.startEditing(newCell[0], newCell[1]);
57989         }
57990     }
57991 });/*
57992  * Based on:
57993  * Ext JS Library 1.1.1
57994  * Copyright(c) 2006-2007, Ext JS, LLC.
57995  *
57996  * Originally Released Under LGPL - original licence link has changed is not relivant.
57997  *
57998  * Fork - LGPL
57999  * <script type="text/javascript">
58000  */
58001 /**
58002  * @class Roo.grid.CellSelectionModel
58003  * @extends Roo.grid.AbstractSelectionModel
58004  * This class provides the basic implementation for cell selection in a grid.
58005  * @constructor
58006  * @param {Object} config The object containing the configuration of this model.
58007  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
58008  */
58009 Roo.grid.CellSelectionModel = function(config){
58010     Roo.apply(this, config);
58011
58012     this.selection = null;
58013
58014     this.addEvents({
58015         /**
58016              * @event beforerowselect
58017              * Fires before a cell is selected.
58018              * @param {SelectionModel} this
58019              * @param {Number} rowIndex The selected row index
58020              * @param {Number} colIndex The selected cell index
58021              */
58022             "beforecellselect" : true,
58023         /**
58024              * @event cellselect
58025              * Fires when a cell is selected.
58026              * @param {SelectionModel} this
58027              * @param {Number} rowIndex The selected row index
58028              * @param {Number} colIndex The selected cell index
58029              */
58030             "cellselect" : true,
58031         /**
58032              * @event selectionchange
58033              * Fires when the active selection changes.
58034              * @param {SelectionModel} this
58035              * @param {Object} selection null for no selection or an object (o) with two properties
58036                 <ul>
58037                 <li>o.record: the record object for the row the selection is in</li>
58038                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
58039                 </ul>
58040              */
58041             "selectionchange" : true,
58042         /**
58043              * @event tabend
58044              * Fires when the tab (or enter) was pressed on the last editable cell
58045              * You can use this to trigger add new row.
58046              * @param {SelectionModel} this
58047              */
58048             "tabend" : true,
58049          /**
58050              * @event beforeeditnext
58051              * Fires before the next editable sell is made active
58052              * You can use this to skip to another cell or fire the tabend
58053              *    if you set cell to false
58054              * @param {Object} eventdata object : { cell : [ row, col ] } 
58055              */
58056             "beforeeditnext" : true
58057     });
58058     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
58059 };
58060
58061 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
58062     
58063     enter_is_tab: false,
58064
58065     /** @ignore */
58066     initEvents : function(){
58067         this.grid.on("mousedown", this.handleMouseDown, this);
58068         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
58069         var view = this.grid.view;
58070         view.on("refresh", this.onViewChange, this);
58071         view.on("rowupdated", this.onRowUpdated, this);
58072         view.on("beforerowremoved", this.clearSelections, this);
58073         view.on("beforerowsinserted", this.clearSelections, this);
58074         if(this.grid.isEditor){
58075             this.grid.on("beforeedit", this.beforeEdit,  this);
58076         }
58077     },
58078
58079         //private
58080     beforeEdit : function(e){
58081         this.select(e.row, e.column, false, true, e.record);
58082     },
58083
58084         //private
58085     onRowUpdated : function(v, index, r){
58086         if(this.selection && this.selection.record == r){
58087             v.onCellSelect(index, this.selection.cell[1]);
58088         }
58089     },
58090
58091         //private
58092     onViewChange : function(){
58093         this.clearSelections(true);
58094     },
58095
58096         /**
58097          * Returns the currently selected cell,.
58098          * @return {Array} The selected cell (row, column) or null if none selected.
58099          */
58100     getSelectedCell : function(){
58101         return this.selection ? this.selection.cell : null;
58102     },
58103
58104     /**
58105      * Clears all selections.
58106      * @param {Boolean} true to prevent the gridview from being notified about the change.
58107      */
58108     clearSelections : function(preventNotify){
58109         var s = this.selection;
58110         if(s){
58111             if(preventNotify !== true){
58112                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
58113             }
58114             this.selection = null;
58115             this.fireEvent("selectionchange", this, null);
58116         }
58117     },
58118
58119     /**
58120      * Returns true if there is a selection.
58121      * @return {Boolean}
58122      */
58123     hasSelection : function(){
58124         return this.selection ? true : false;
58125     },
58126
58127     /** @ignore */
58128     handleMouseDown : function(e, t){
58129         var v = this.grid.getView();
58130         if(this.isLocked()){
58131             return;
58132         };
58133         var row = v.findRowIndex(t);
58134         var cell = v.findCellIndex(t);
58135         if(row !== false && cell !== false){
58136             this.select(row, cell);
58137         }
58138     },
58139
58140     /**
58141      * Selects a cell.
58142      * @param {Number} rowIndex
58143      * @param {Number} collIndex
58144      */
58145     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
58146         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
58147             this.clearSelections();
58148             r = r || this.grid.dataSource.getAt(rowIndex);
58149             this.selection = {
58150                 record : r,
58151                 cell : [rowIndex, colIndex]
58152             };
58153             if(!preventViewNotify){
58154                 var v = this.grid.getView();
58155                 v.onCellSelect(rowIndex, colIndex);
58156                 if(preventFocus !== true){
58157                     v.focusCell(rowIndex, colIndex);
58158                 }
58159             }
58160             this.fireEvent("cellselect", this, rowIndex, colIndex);
58161             this.fireEvent("selectionchange", this, this.selection);
58162         }
58163     },
58164
58165         //private
58166     isSelectable : function(rowIndex, colIndex, cm){
58167         return !cm.isHidden(colIndex);
58168     },
58169
58170     /** @ignore */
58171     handleKeyDown : function(e){
58172         //Roo.log('Cell Sel Model handleKeyDown');
58173         if(!e.isNavKeyPress()){
58174             return;
58175         }
58176         var g = this.grid, s = this.selection;
58177         if(!s){
58178             e.stopEvent();
58179             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
58180             if(cell){
58181                 this.select(cell[0], cell[1]);
58182             }
58183             return;
58184         }
58185         var sm = this;
58186         var walk = function(row, col, step){
58187             return g.walkCells(row, col, step, sm.isSelectable,  sm);
58188         };
58189         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
58190         var newCell;
58191
58192       
58193
58194         switch(k){
58195             case e.TAB:
58196                 // handled by onEditorKey
58197                 if (g.isEditor && g.editing) {
58198                     return;
58199                 }
58200                 if(e.shiftKey) {
58201                     newCell = walk(r, c-1, -1);
58202                 } else {
58203                     newCell = walk(r, c+1, 1);
58204                 }
58205                 break;
58206             
58207             case e.DOWN:
58208                newCell = walk(r+1, c, 1);
58209                 break;
58210             
58211             case e.UP:
58212                 newCell = walk(r-1, c, -1);
58213                 break;
58214             
58215             case e.RIGHT:
58216                 newCell = walk(r, c+1, 1);
58217                 break;
58218             
58219             case e.LEFT:
58220                 newCell = walk(r, c-1, -1);
58221                 break;
58222             
58223             case e.ENTER:
58224                 
58225                 if(g.isEditor && !g.editing){
58226                    g.startEditing(r, c);
58227                    e.stopEvent();
58228                    return;
58229                 }
58230                 
58231                 
58232              break;
58233         };
58234         if(newCell){
58235             this.select(newCell[0], newCell[1]);
58236             e.stopEvent();
58237             
58238         }
58239     },
58240
58241     acceptsNav : function(row, col, cm){
58242         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58243     },
58244     /**
58245      * Selects a cell.
58246      * @param {Number} field (not used) - as it's normally used as a listener
58247      * @param {Number} e - event - fake it by using
58248      *
58249      * var e = Roo.EventObjectImpl.prototype;
58250      * e.keyCode = e.TAB
58251      *
58252      * 
58253      */
58254     onEditorKey : function(field, e){
58255         
58256         var k = e.getKey(),
58257             newCell,
58258             g = this.grid,
58259             ed = g.activeEditor,
58260             forward = false;
58261         ///Roo.log('onEditorKey' + k);
58262         
58263         
58264         if (this.enter_is_tab && k == e.ENTER) {
58265             k = e.TAB;
58266         }
58267         
58268         if(k == e.TAB){
58269             if(e.shiftKey){
58270                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58271             }else{
58272                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58273                 forward = true;
58274             }
58275             
58276             e.stopEvent();
58277             
58278         } else if(k == e.ENTER &&  !e.ctrlKey){
58279             ed.completeEdit();
58280             e.stopEvent();
58281             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58282         
58283                 } else if(k == e.ESC){
58284             ed.cancelEdit();
58285         }
58286                 
58287         if (newCell) {
58288             var ecall = { cell : newCell, forward : forward };
58289             this.fireEvent('beforeeditnext', ecall );
58290             newCell = ecall.cell;
58291                         forward = ecall.forward;
58292         }
58293                 
58294         if(newCell){
58295             //Roo.log('next cell after edit');
58296             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
58297         } else if (forward) {
58298             // tabbed past last
58299             this.fireEvent.defer(100, this, ['tabend',this]);
58300         }
58301     }
58302 });/*
58303  * Based on:
58304  * Ext JS Library 1.1.1
58305  * Copyright(c) 2006-2007, Ext JS, LLC.
58306  *
58307  * Originally Released Under LGPL - original licence link has changed is not relivant.
58308  *
58309  * Fork - LGPL
58310  * <script type="text/javascript">
58311  */
58312  
58313 /**
58314  * @class Roo.grid.EditorGrid
58315  * @extends Roo.grid.Grid
58316  * Class for creating and editable grid.
58317  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
58318  * The container MUST have some type of size defined for the grid to fill. The container will be 
58319  * automatically set to position relative if it isn't already.
58320  * @param {Object} dataSource The data model to bind to
58321  * @param {Object} colModel The column model with info about this grid's columns
58322  */
58323 Roo.grid.EditorGrid = function(container, config){
58324     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
58325     this.getGridEl().addClass("xedit-grid");
58326
58327     if(!this.selModel){
58328         this.selModel = new Roo.grid.CellSelectionModel();
58329     }
58330
58331     this.activeEditor = null;
58332
58333         this.addEvents({
58334             /**
58335              * @event beforeedit
58336              * Fires before cell editing is triggered. The edit event object has the following properties <br />
58337              * <ul style="padding:5px;padding-left:16px;">
58338              * <li>grid - This grid</li>
58339              * <li>record - The record being edited</li>
58340              * <li>field - The field name being edited</li>
58341              * <li>value - The value for the field being edited.</li>
58342              * <li>row - The grid row index</li>
58343              * <li>column - The grid column index</li>
58344              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58345              * </ul>
58346              * @param {Object} e An edit event (see above for description)
58347              */
58348             "beforeedit" : true,
58349             /**
58350              * @event afteredit
58351              * Fires after a cell is edited. <br />
58352              * <ul style="padding:5px;padding-left:16px;">
58353              * <li>grid - This grid</li>
58354              * <li>record - The record being edited</li>
58355              * <li>field - The field name being edited</li>
58356              * <li>value - The value being set</li>
58357              * <li>originalValue - The original value for the field, before the edit.</li>
58358              * <li>row - The grid row index</li>
58359              * <li>column - The grid column index</li>
58360              * </ul>
58361              * @param {Object} e An edit event (see above for description)
58362              */
58363             "afteredit" : true,
58364             /**
58365              * @event validateedit
58366              * Fires after a cell is edited, but before the value is set in the record. 
58367          * You can use this to modify the value being set in the field, Return false
58368              * to cancel the change. The edit event object has the following properties <br />
58369              * <ul style="padding:5px;padding-left:16px;">
58370          * <li>editor - This editor</li>
58371              * <li>grid - This grid</li>
58372              * <li>record - The record being edited</li>
58373              * <li>field - The field name being edited</li>
58374              * <li>value - The value being set</li>
58375              * <li>originalValue - The original value for the field, before the edit.</li>
58376              * <li>row - The grid row index</li>
58377              * <li>column - The grid column index</li>
58378              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58379              * </ul>
58380              * @param {Object} e An edit event (see above for description)
58381              */
58382             "validateedit" : true
58383         });
58384     this.on("bodyscroll", this.stopEditing,  this);
58385     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
58386 };
58387
58388 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
58389     /**
58390      * @cfg {Number} clicksToEdit
58391      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
58392      */
58393     clicksToEdit: 2,
58394
58395     // private
58396     isEditor : true,
58397     // private
58398     trackMouseOver: false, // causes very odd FF errors
58399
58400     onCellDblClick : function(g, row, col){
58401         this.startEditing(row, col);
58402     },
58403
58404     onEditComplete : function(ed, value, startValue){
58405         this.editing = false;
58406         this.activeEditor = null;
58407         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
58408         var r = ed.record;
58409         var field = this.colModel.getDataIndex(ed.col);
58410         var e = {
58411             grid: this,
58412             record: r,
58413             field: field,
58414             originalValue: startValue,
58415             value: value,
58416             row: ed.row,
58417             column: ed.col,
58418             cancel:false,
58419             editor: ed
58420         };
58421         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
58422         cell.show();
58423           
58424         if(String(value) !== String(startValue)){
58425             
58426             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
58427                 r.set(field, e.value);
58428                 // if we are dealing with a combo box..
58429                 // then we also set the 'name' colum to be the displayField
58430                 if (ed.field.displayField && ed.field.name) {
58431                     r.set(ed.field.name, ed.field.el.dom.value);
58432                 }
58433                 
58434                 delete e.cancel; //?? why!!!
58435                 this.fireEvent("afteredit", e);
58436             }
58437         } else {
58438             this.fireEvent("afteredit", e); // always fire it!
58439         }
58440         this.view.focusCell(ed.row, ed.col);
58441     },
58442
58443     /**
58444      * Starts editing the specified for the specified row/column
58445      * @param {Number} rowIndex
58446      * @param {Number} colIndex
58447      */
58448     startEditing : function(row, col){
58449         this.stopEditing();
58450         if(this.colModel.isCellEditable(col, row)){
58451             this.view.ensureVisible(row, col, true);
58452           
58453             var r = this.dataSource.getAt(row);
58454             var field = this.colModel.getDataIndex(col);
58455             var cell = Roo.get(this.view.getCell(row,col));
58456             var e = {
58457                 grid: this,
58458                 record: r,
58459                 field: field,
58460                 value: r.data[field],
58461                 row: row,
58462                 column: col,
58463                 cancel:false 
58464             };
58465             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
58466                 this.editing = true;
58467                 var ed = this.colModel.getCellEditor(col, row);
58468                 
58469                 if (!ed) {
58470                     return;
58471                 }
58472                 if(!ed.rendered){
58473                     ed.render(ed.parentEl || document.body);
58474                 }
58475                 ed.field.reset();
58476                
58477                 cell.hide();
58478                 
58479                 (function(){ // complex but required for focus issues in safari, ie and opera
58480                     ed.row = row;
58481                     ed.col = col;
58482                     ed.record = r;
58483                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
58484                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
58485                     this.activeEditor = ed;
58486                     var v = r.data[field];
58487                     ed.startEdit(this.view.getCell(row, col), v);
58488                     // combo's with 'displayField and name set
58489                     if (ed.field.displayField && ed.field.name) {
58490                         ed.field.el.dom.value = r.data[ed.field.name];
58491                     }
58492                     
58493                     
58494                 }).defer(50, this);
58495             }
58496         }
58497     },
58498         
58499     /**
58500      * Stops any active editing
58501      */
58502     stopEditing : function(){
58503         if(this.activeEditor){
58504             this.activeEditor.completeEdit();
58505         }
58506         this.activeEditor = null;
58507     },
58508         
58509          /**
58510      * Called to get grid's drag proxy text, by default returns this.ddText.
58511      * @return {String}
58512      */
58513     getDragDropText : function(){
58514         var count = this.selModel.getSelectedCell() ? 1 : 0;
58515         return String.format(this.ddText, count, count == 1 ? '' : 's');
58516     }
58517         
58518 });/*
58519  * Based on:
58520  * Ext JS Library 1.1.1
58521  * Copyright(c) 2006-2007, Ext JS, LLC.
58522  *
58523  * Originally Released Under LGPL - original licence link has changed is not relivant.
58524  *
58525  * Fork - LGPL
58526  * <script type="text/javascript">
58527  */
58528
58529 // private - not really -- you end up using it !
58530 // This is a support class used internally by the Grid components
58531
58532 /**
58533  * @class Roo.grid.GridEditor
58534  * @extends Roo.Editor
58535  * Class for creating and editable grid elements.
58536  * @param {Object} config any settings (must include field)
58537  */
58538 Roo.grid.GridEditor = function(field, config){
58539     if (!config && field.field) {
58540         config = field;
58541         field = Roo.factory(config.field, Roo.form);
58542     }
58543     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
58544     field.monitorTab = false;
58545 };
58546
58547 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
58548     
58549     /**
58550      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
58551      */
58552     
58553     alignment: "tl-tl",
58554     autoSize: "width",
58555     hideEl : false,
58556     cls: "x-small-editor x-grid-editor",
58557     shim:false,
58558     shadow:"frame"
58559 });/*
58560  * Based on:
58561  * Ext JS Library 1.1.1
58562  * Copyright(c) 2006-2007, Ext JS, LLC.
58563  *
58564  * Originally Released Under LGPL - original licence link has changed is not relivant.
58565  *
58566  * Fork - LGPL
58567  * <script type="text/javascript">
58568  */
58569   
58570
58571   
58572 Roo.grid.PropertyRecord = Roo.data.Record.create([
58573     {name:'name',type:'string'},  'value'
58574 ]);
58575
58576
58577 Roo.grid.PropertyStore = function(grid, source){
58578     this.grid = grid;
58579     this.store = new Roo.data.Store({
58580         recordType : Roo.grid.PropertyRecord
58581     });
58582     this.store.on('update', this.onUpdate,  this);
58583     if(source){
58584         this.setSource(source);
58585     }
58586     Roo.grid.PropertyStore.superclass.constructor.call(this);
58587 };
58588
58589
58590
58591 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
58592     setSource : function(o){
58593         this.source = o;
58594         this.store.removeAll();
58595         var data = [];
58596         for(var k in o){
58597             if(this.isEditableValue(o[k])){
58598                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
58599             }
58600         }
58601         this.store.loadRecords({records: data}, {}, true);
58602     },
58603
58604     onUpdate : function(ds, record, type){
58605         if(type == Roo.data.Record.EDIT){
58606             var v = record.data['value'];
58607             var oldValue = record.modified['value'];
58608             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
58609                 this.source[record.id] = v;
58610                 record.commit();
58611                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
58612             }else{
58613                 record.reject();
58614             }
58615         }
58616     },
58617
58618     getProperty : function(row){
58619        return this.store.getAt(row);
58620     },
58621
58622     isEditableValue: function(val){
58623         if(val && val instanceof Date){
58624             return true;
58625         }else if(typeof val == 'object' || typeof val == 'function'){
58626             return false;
58627         }
58628         return true;
58629     },
58630
58631     setValue : function(prop, value){
58632         this.source[prop] = value;
58633         this.store.getById(prop).set('value', value);
58634     },
58635
58636     getSource : function(){
58637         return this.source;
58638     }
58639 });
58640
58641 Roo.grid.PropertyColumnModel = function(grid, store){
58642     this.grid = grid;
58643     var g = Roo.grid;
58644     g.PropertyColumnModel.superclass.constructor.call(this, [
58645         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
58646         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
58647     ]);
58648     this.store = store;
58649     this.bselect = Roo.DomHelper.append(document.body, {
58650         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
58651             {tag: 'option', value: 'true', html: 'true'},
58652             {tag: 'option', value: 'false', html: 'false'}
58653         ]
58654     });
58655     Roo.id(this.bselect);
58656     var f = Roo.form;
58657     this.editors = {
58658         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
58659         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
58660         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
58661         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
58662         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
58663     };
58664     this.renderCellDelegate = this.renderCell.createDelegate(this);
58665     this.renderPropDelegate = this.renderProp.createDelegate(this);
58666 };
58667
58668 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
58669     
58670     
58671     nameText : 'Name',
58672     valueText : 'Value',
58673     
58674     dateFormat : 'm/j/Y',
58675     
58676     
58677     renderDate : function(dateVal){
58678         return dateVal.dateFormat(this.dateFormat);
58679     },
58680
58681     renderBool : function(bVal){
58682         return bVal ? 'true' : 'false';
58683     },
58684
58685     isCellEditable : function(colIndex, rowIndex){
58686         return colIndex == 1;
58687     },
58688
58689     getRenderer : function(col){
58690         return col == 1 ?
58691             this.renderCellDelegate : this.renderPropDelegate;
58692     },
58693
58694     renderProp : function(v){
58695         return this.getPropertyName(v);
58696     },
58697
58698     renderCell : function(val){
58699         var rv = val;
58700         if(val instanceof Date){
58701             rv = this.renderDate(val);
58702         }else if(typeof val == 'boolean'){
58703             rv = this.renderBool(val);
58704         }
58705         return Roo.util.Format.htmlEncode(rv);
58706     },
58707
58708     getPropertyName : function(name){
58709         var pn = this.grid.propertyNames;
58710         return pn && pn[name] ? pn[name] : name;
58711     },
58712
58713     getCellEditor : function(colIndex, rowIndex){
58714         var p = this.store.getProperty(rowIndex);
58715         var n = p.data['name'], val = p.data['value'];
58716         
58717         if(typeof(this.grid.customEditors[n]) == 'string'){
58718             return this.editors[this.grid.customEditors[n]];
58719         }
58720         if(typeof(this.grid.customEditors[n]) != 'undefined'){
58721             return this.grid.customEditors[n];
58722         }
58723         if(val instanceof Date){
58724             return this.editors['date'];
58725         }else if(typeof val == 'number'){
58726             return this.editors['number'];
58727         }else if(typeof val == 'boolean'){
58728             return this.editors['boolean'];
58729         }else{
58730             return this.editors['string'];
58731         }
58732     }
58733 });
58734
58735 /**
58736  * @class Roo.grid.PropertyGrid
58737  * @extends Roo.grid.EditorGrid
58738  * This class represents the  interface of a component based property grid control.
58739  * <br><br>Usage:<pre><code>
58740  var grid = new Roo.grid.PropertyGrid("my-container-id", {
58741       
58742  });
58743  // set any options
58744  grid.render();
58745  * </code></pre>
58746   
58747  * @constructor
58748  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58749  * The container MUST have some type of size defined for the grid to fill. The container will be
58750  * automatically set to position relative if it isn't already.
58751  * @param {Object} config A config object that sets properties on this grid.
58752  */
58753 Roo.grid.PropertyGrid = function(container, config){
58754     config = config || {};
58755     var store = new Roo.grid.PropertyStore(this);
58756     this.store = store;
58757     var cm = new Roo.grid.PropertyColumnModel(this, store);
58758     store.store.sort('name', 'ASC');
58759     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
58760         ds: store.store,
58761         cm: cm,
58762         enableColLock:false,
58763         enableColumnMove:false,
58764         stripeRows:false,
58765         trackMouseOver: false,
58766         clicksToEdit:1
58767     }, config));
58768     this.getGridEl().addClass('x-props-grid');
58769     this.lastEditRow = null;
58770     this.on('columnresize', this.onColumnResize, this);
58771     this.addEvents({
58772          /**
58773              * @event beforepropertychange
58774              * Fires before a property changes (return false to stop?)
58775              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58776              * @param {String} id Record Id
58777              * @param {String} newval New Value
58778          * @param {String} oldval Old Value
58779              */
58780         "beforepropertychange": true,
58781         /**
58782              * @event propertychange
58783              * Fires after a property changes
58784              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58785              * @param {String} id Record Id
58786              * @param {String} newval New Value
58787          * @param {String} oldval Old Value
58788              */
58789         "propertychange": true
58790     });
58791     this.customEditors = this.customEditors || {};
58792 };
58793 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
58794     
58795      /**
58796      * @cfg {Object} customEditors map of colnames=> custom editors.
58797      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
58798      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
58799      * false disables editing of the field.
58800          */
58801     
58802       /**
58803      * @cfg {Object} propertyNames map of property Names to their displayed value
58804          */
58805     
58806     render : function(){
58807         Roo.grid.PropertyGrid.superclass.render.call(this);
58808         this.autoSize.defer(100, this);
58809     },
58810
58811     autoSize : function(){
58812         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
58813         if(this.view){
58814             this.view.fitColumns();
58815         }
58816     },
58817
58818     onColumnResize : function(){
58819         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
58820         this.autoSize();
58821     },
58822     /**
58823      * Sets the data for the Grid
58824      * accepts a Key => Value object of all the elements avaiable.
58825      * @param {Object} data  to appear in grid.
58826      */
58827     setSource : function(source){
58828         this.store.setSource(source);
58829         //this.autoSize();
58830     },
58831     /**
58832      * Gets all the data from the grid.
58833      * @return {Object} data  data stored in grid
58834      */
58835     getSource : function(){
58836         return this.store.getSource();
58837     }
58838 });/*
58839   
58840  * Licence LGPL
58841  
58842  */
58843  
58844 /**
58845  * @class Roo.grid.Calendar
58846  * @extends Roo.util.Grid
58847  * This class extends the Grid to provide a calendar widget
58848  * <br><br>Usage:<pre><code>
58849  var grid = new Roo.grid.Calendar("my-container-id", {
58850      ds: myDataStore,
58851      cm: myColModel,
58852      selModel: mySelectionModel,
58853      autoSizeColumns: true,
58854      monitorWindowResize: false,
58855      trackMouseOver: true
58856      eventstore : real data store..
58857  });
58858  // set any options
58859  grid.render();
58860   
58861   * @constructor
58862  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58863  * The container MUST have some type of size defined for the grid to fill. The container will be
58864  * automatically set to position relative if it isn't already.
58865  * @param {Object} config A config object that sets properties on this grid.
58866  */
58867 Roo.grid.Calendar = function(container, config){
58868         // initialize the container
58869         this.container = Roo.get(container);
58870         this.container.update("");
58871         this.container.setStyle("overflow", "hidden");
58872     this.container.addClass('x-grid-container');
58873
58874     this.id = this.container.id;
58875
58876     Roo.apply(this, config);
58877     // check and correct shorthanded configs
58878     
58879     var rows = [];
58880     var d =1;
58881     for (var r = 0;r < 6;r++) {
58882         
58883         rows[r]=[];
58884         for (var c =0;c < 7;c++) {
58885             rows[r][c]= '';
58886         }
58887     }
58888     if (this.eventStore) {
58889         this.eventStore= Roo.factory(this.eventStore, Roo.data);
58890         this.eventStore.on('load',this.onLoad, this);
58891         this.eventStore.on('beforeload',this.clearEvents, this);
58892          
58893     }
58894     
58895     this.dataSource = new Roo.data.Store({
58896             proxy: new Roo.data.MemoryProxy(rows),
58897             reader: new Roo.data.ArrayReader({}, [
58898                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
58899     });
58900
58901     this.dataSource.load();
58902     this.ds = this.dataSource;
58903     this.ds.xmodule = this.xmodule || false;
58904     
58905     
58906     var cellRender = function(v,x,r)
58907     {
58908         return String.format(
58909             '<div class="fc-day  fc-widget-content"><div>' +
58910                 '<div class="fc-event-container"></div>' +
58911                 '<div class="fc-day-number">{0}</div>'+
58912                 
58913                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
58914             '</div></div>', v);
58915     
58916     }
58917     
58918     
58919     this.colModel = new Roo.grid.ColumnModel( [
58920         {
58921             xtype: 'ColumnModel',
58922             xns: Roo.grid,
58923             dataIndex : 'weekday0',
58924             header : 'Sunday',
58925             renderer : cellRender
58926         },
58927         {
58928             xtype: 'ColumnModel',
58929             xns: Roo.grid,
58930             dataIndex : 'weekday1',
58931             header : 'Monday',
58932             renderer : cellRender
58933         },
58934         {
58935             xtype: 'ColumnModel',
58936             xns: Roo.grid,
58937             dataIndex : 'weekday2',
58938             header : 'Tuesday',
58939             renderer : cellRender
58940         },
58941         {
58942             xtype: 'ColumnModel',
58943             xns: Roo.grid,
58944             dataIndex : 'weekday3',
58945             header : 'Wednesday',
58946             renderer : cellRender
58947         },
58948         {
58949             xtype: 'ColumnModel',
58950             xns: Roo.grid,
58951             dataIndex : 'weekday4',
58952             header : 'Thursday',
58953             renderer : cellRender
58954         },
58955         {
58956             xtype: 'ColumnModel',
58957             xns: Roo.grid,
58958             dataIndex : 'weekday5',
58959             header : 'Friday',
58960             renderer : cellRender
58961         },
58962         {
58963             xtype: 'ColumnModel',
58964             xns: Roo.grid,
58965             dataIndex : 'weekday6',
58966             header : 'Saturday',
58967             renderer : cellRender
58968         }
58969     ]);
58970     this.cm = this.colModel;
58971     this.cm.xmodule = this.xmodule || false;
58972  
58973         
58974           
58975     //this.selModel = new Roo.grid.CellSelectionModel();
58976     //this.sm = this.selModel;
58977     //this.selModel.init(this);
58978     
58979     
58980     if(this.width){
58981         this.container.setWidth(this.width);
58982     }
58983
58984     if(this.height){
58985         this.container.setHeight(this.height);
58986     }
58987     /** @private */
58988         this.addEvents({
58989         // raw events
58990         /**
58991          * @event click
58992          * The raw click event for the entire grid.
58993          * @param {Roo.EventObject} e
58994          */
58995         "click" : true,
58996         /**
58997          * @event dblclick
58998          * The raw dblclick event for the entire grid.
58999          * @param {Roo.EventObject} e
59000          */
59001         "dblclick" : true,
59002         /**
59003          * @event contextmenu
59004          * The raw contextmenu event for the entire grid.
59005          * @param {Roo.EventObject} e
59006          */
59007         "contextmenu" : true,
59008         /**
59009          * @event mousedown
59010          * The raw mousedown event for the entire grid.
59011          * @param {Roo.EventObject} e
59012          */
59013         "mousedown" : true,
59014         /**
59015          * @event mouseup
59016          * The raw mouseup event for the entire grid.
59017          * @param {Roo.EventObject} e
59018          */
59019         "mouseup" : true,
59020         /**
59021          * @event mouseover
59022          * The raw mouseover event for the entire grid.
59023          * @param {Roo.EventObject} e
59024          */
59025         "mouseover" : true,
59026         /**
59027          * @event mouseout
59028          * The raw mouseout event for the entire grid.
59029          * @param {Roo.EventObject} e
59030          */
59031         "mouseout" : true,
59032         /**
59033          * @event keypress
59034          * The raw keypress event for the entire grid.
59035          * @param {Roo.EventObject} e
59036          */
59037         "keypress" : true,
59038         /**
59039          * @event keydown
59040          * The raw keydown event for the entire grid.
59041          * @param {Roo.EventObject} e
59042          */
59043         "keydown" : true,
59044
59045         // custom events
59046
59047         /**
59048          * @event cellclick
59049          * Fires when a cell is clicked
59050          * @param {Grid} this
59051          * @param {Number} rowIndex
59052          * @param {Number} columnIndex
59053          * @param {Roo.EventObject} e
59054          */
59055         "cellclick" : true,
59056         /**
59057          * @event celldblclick
59058          * Fires when a cell is double clicked
59059          * @param {Grid} this
59060          * @param {Number} rowIndex
59061          * @param {Number} columnIndex
59062          * @param {Roo.EventObject} e
59063          */
59064         "celldblclick" : true,
59065         /**
59066          * @event rowclick
59067          * Fires when a row is clicked
59068          * @param {Grid} this
59069          * @param {Number} rowIndex
59070          * @param {Roo.EventObject} e
59071          */
59072         "rowclick" : true,
59073         /**
59074          * @event rowdblclick
59075          * Fires when a row is double clicked
59076          * @param {Grid} this
59077          * @param {Number} rowIndex
59078          * @param {Roo.EventObject} e
59079          */
59080         "rowdblclick" : true,
59081         /**
59082          * @event headerclick
59083          * Fires when a header is clicked
59084          * @param {Grid} this
59085          * @param {Number} columnIndex
59086          * @param {Roo.EventObject} e
59087          */
59088         "headerclick" : true,
59089         /**
59090          * @event headerdblclick
59091          * Fires when a header cell is double clicked
59092          * @param {Grid} this
59093          * @param {Number} columnIndex
59094          * @param {Roo.EventObject} e
59095          */
59096         "headerdblclick" : true,
59097         /**
59098          * @event rowcontextmenu
59099          * Fires when a row is right clicked
59100          * @param {Grid} this
59101          * @param {Number} rowIndex
59102          * @param {Roo.EventObject} e
59103          */
59104         "rowcontextmenu" : true,
59105         /**
59106          * @event cellcontextmenu
59107          * Fires when a cell is right clicked
59108          * @param {Grid} this
59109          * @param {Number} rowIndex
59110          * @param {Number} cellIndex
59111          * @param {Roo.EventObject} e
59112          */
59113          "cellcontextmenu" : true,
59114         /**
59115          * @event headercontextmenu
59116          * Fires when a header is right clicked
59117          * @param {Grid} this
59118          * @param {Number} columnIndex
59119          * @param {Roo.EventObject} e
59120          */
59121         "headercontextmenu" : true,
59122         /**
59123          * @event bodyscroll
59124          * Fires when the body element is scrolled
59125          * @param {Number} scrollLeft
59126          * @param {Number} scrollTop
59127          */
59128         "bodyscroll" : true,
59129         /**
59130          * @event columnresize
59131          * Fires when the user resizes a column
59132          * @param {Number} columnIndex
59133          * @param {Number} newSize
59134          */
59135         "columnresize" : true,
59136         /**
59137          * @event columnmove
59138          * Fires when the user moves a column
59139          * @param {Number} oldIndex
59140          * @param {Number} newIndex
59141          */
59142         "columnmove" : true,
59143         /**
59144          * @event startdrag
59145          * Fires when row(s) start being dragged
59146          * @param {Grid} this
59147          * @param {Roo.GridDD} dd The drag drop object
59148          * @param {event} e The raw browser event
59149          */
59150         "startdrag" : true,
59151         /**
59152          * @event enddrag
59153          * Fires when a drag operation is complete
59154          * @param {Grid} this
59155          * @param {Roo.GridDD} dd The drag drop object
59156          * @param {event} e The raw browser event
59157          */
59158         "enddrag" : true,
59159         /**
59160          * @event dragdrop
59161          * Fires when dragged row(s) are dropped on a valid DD target
59162          * @param {Grid} this
59163          * @param {Roo.GridDD} dd The drag drop object
59164          * @param {String} targetId The target drag drop object
59165          * @param {event} e The raw browser event
59166          */
59167         "dragdrop" : true,
59168         /**
59169          * @event dragover
59170          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
59171          * @param {Grid} this
59172          * @param {Roo.GridDD} dd The drag drop object
59173          * @param {String} targetId The target drag drop object
59174          * @param {event} e The raw browser event
59175          */
59176         "dragover" : true,
59177         /**
59178          * @event dragenter
59179          *  Fires when the dragged row(s) first cross another DD target while being dragged
59180          * @param {Grid} this
59181          * @param {Roo.GridDD} dd The drag drop object
59182          * @param {String} targetId The target drag drop object
59183          * @param {event} e The raw browser event
59184          */
59185         "dragenter" : true,
59186         /**
59187          * @event dragout
59188          * Fires when the dragged row(s) leave another DD target while being dragged
59189          * @param {Grid} this
59190          * @param {Roo.GridDD} dd The drag drop object
59191          * @param {String} targetId The target drag drop object
59192          * @param {event} e The raw browser event
59193          */
59194         "dragout" : true,
59195         /**
59196          * @event rowclass
59197          * Fires when a row is rendered, so you can change add a style to it.
59198          * @param {GridView} gridview   The grid view
59199          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
59200          */
59201         'rowclass' : true,
59202
59203         /**
59204          * @event render
59205          * Fires when the grid is rendered
59206          * @param {Grid} grid
59207          */
59208         'render' : true,
59209             /**
59210              * @event select
59211              * Fires when a date is selected
59212              * @param {DatePicker} this
59213              * @param {Date} date The selected date
59214              */
59215         'select': true,
59216         /**
59217              * @event monthchange
59218              * Fires when the displayed month changes 
59219              * @param {DatePicker} this
59220              * @param {Date} date The selected month
59221              */
59222         'monthchange': true,
59223         /**
59224              * @event evententer
59225              * Fires when mouse over an event
59226              * @param {Calendar} this
59227              * @param {event} Event
59228              */
59229         'evententer': true,
59230         /**
59231              * @event eventleave
59232              * Fires when the mouse leaves an
59233              * @param {Calendar} this
59234              * @param {event}
59235              */
59236         'eventleave': true,
59237         /**
59238              * @event eventclick
59239              * Fires when the mouse click an
59240              * @param {Calendar} this
59241              * @param {event}
59242              */
59243         'eventclick': true,
59244         /**
59245              * @event eventrender
59246              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
59247              * @param {Calendar} this
59248              * @param {data} data to be modified
59249              */
59250         'eventrender': true
59251         
59252     });
59253
59254     Roo.grid.Grid.superclass.constructor.call(this);
59255     this.on('render', function() {
59256         this.view.el.addClass('x-grid-cal'); 
59257         
59258         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
59259
59260     },this);
59261     
59262     if (!Roo.grid.Calendar.style) {
59263         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
59264             
59265             
59266             '.x-grid-cal .x-grid-col' :  {
59267                 height: 'auto !important',
59268                 'vertical-align': 'top'
59269             },
59270             '.x-grid-cal  .fc-event-hori' : {
59271                 height: '14px'
59272             }
59273              
59274             
59275         }, Roo.id());
59276     }
59277
59278     
59279     
59280 };
59281 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
59282     /**
59283      * @cfg {Store} eventStore The store that loads events.
59284      */
59285     eventStore : 25,
59286
59287      
59288     activeDate : false,
59289     startDay : 0,
59290     autoWidth : true,
59291     monitorWindowResize : false,
59292
59293     
59294     resizeColumns : function() {
59295         var col = (this.view.el.getWidth() / 7) - 3;
59296         // loop through cols, and setWidth
59297         for(var i =0 ; i < 7 ; i++){
59298             this.cm.setColumnWidth(i, col);
59299         }
59300     },
59301      setDate :function(date) {
59302         
59303         Roo.log('setDate?');
59304         
59305         this.resizeColumns();
59306         var vd = this.activeDate;
59307         this.activeDate = date;
59308 //        if(vd && this.el){
59309 //            var t = date.getTime();
59310 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
59311 //                Roo.log('using add remove');
59312 //                
59313 //                this.fireEvent('monthchange', this, date);
59314 //                
59315 //                this.cells.removeClass("fc-state-highlight");
59316 //                this.cells.each(function(c){
59317 //                   if(c.dateValue == t){
59318 //                       c.addClass("fc-state-highlight");
59319 //                       setTimeout(function(){
59320 //                            try{c.dom.firstChild.focus();}catch(e){}
59321 //                       }, 50);
59322 //                       return false;
59323 //                   }
59324 //                   return true;
59325 //                });
59326 //                return;
59327 //            }
59328 //        }
59329         
59330         var days = date.getDaysInMonth();
59331         
59332         var firstOfMonth = date.getFirstDateOfMonth();
59333         var startingPos = firstOfMonth.getDay()-this.startDay;
59334         
59335         if(startingPos < this.startDay){
59336             startingPos += 7;
59337         }
59338         
59339         var pm = date.add(Date.MONTH, -1);
59340         var prevStart = pm.getDaysInMonth()-startingPos;
59341 //        
59342         
59343         
59344         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59345         
59346         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
59347         //this.cells.addClassOnOver('fc-state-hover');
59348         
59349         var cells = this.cells.elements;
59350         var textEls = this.textNodes;
59351         
59352         //Roo.each(cells, function(cell){
59353         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
59354         //});
59355         
59356         days += startingPos;
59357
59358         // convert everything to numbers so it's fast
59359         var day = 86400000;
59360         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
59361         //Roo.log(d);
59362         //Roo.log(pm);
59363         //Roo.log(prevStart);
59364         
59365         var today = new Date().clearTime().getTime();
59366         var sel = date.clearTime().getTime();
59367         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
59368         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
59369         var ddMatch = this.disabledDatesRE;
59370         var ddText = this.disabledDatesText;
59371         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
59372         var ddaysText = this.disabledDaysText;
59373         var format = this.format;
59374         
59375         var setCellClass = function(cal, cell){
59376             
59377             //Roo.log('set Cell Class');
59378             cell.title = "";
59379             var t = d.getTime();
59380             
59381             //Roo.log(d);
59382             
59383             
59384             cell.dateValue = t;
59385             if(t == today){
59386                 cell.className += " fc-today";
59387                 cell.className += " fc-state-highlight";
59388                 cell.title = cal.todayText;
59389             }
59390             if(t == sel){
59391                 // disable highlight in other month..
59392                 cell.className += " fc-state-highlight";
59393                 
59394             }
59395             // disabling
59396             if(t < min) {
59397                 //cell.className = " fc-state-disabled";
59398                 cell.title = cal.minText;
59399                 return;
59400             }
59401             if(t > max) {
59402                 //cell.className = " fc-state-disabled";
59403                 cell.title = cal.maxText;
59404                 return;
59405             }
59406             if(ddays){
59407                 if(ddays.indexOf(d.getDay()) != -1){
59408                     // cell.title = ddaysText;
59409                    // cell.className = " fc-state-disabled";
59410                 }
59411             }
59412             if(ddMatch && format){
59413                 var fvalue = d.dateFormat(format);
59414                 if(ddMatch.test(fvalue)){
59415                     cell.title = ddText.replace("%0", fvalue);
59416                    cell.className = " fc-state-disabled";
59417                 }
59418             }
59419             
59420             if (!cell.initialClassName) {
59421                 cell.initialClassName = cell.dom.className;
59422             }
59423             
59424             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
59425         };
59426
59427         var i = 0;
59428         
59429         for(; i < startingPos; i++) {
59430             cells[i].dayName =  (++prevStart);
59431             Roo.log(textEls[i]);
59432             d.setDate(d.getDate()+1);
59433             
59434             //cells[i].className = "fc-past fc-other-month";
59435             setCellClass(this, cells[i]);
59436         }
59437         
59438         var intDay = 0;
59439         
59440         for(; i < days; i++){
59441             intDay = i - startingPos + 1;
59442             cells[i].dayName =  (intDay);
59443             d.setDate(d.getDate()+1);
59444             
59445             cells[i].className = ''; // "x-date-active";
59446             setCellClass(this, cells[i]);
59447         }
59448         var extraDays = 0;
59449         
59450         for(; i < 42; i++) {
59451             //textEls[i].innerHTML = (++extraDays);
59452             
59453             d.setDate(d.getDate()+1);
59454             cells[i].dayName = (++extraDays);
59455             cells[i].className = "fc-future fc-other-month";
59456             setCellClass(this, cells[i]);
59457         }
59458         
59459         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
59460         
59461         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
59462         
59463         // this will cause all the cells to mis
59464         var rows= [];
59465         var i =0;
59466         for (var r = 0;r < 6;r++) {
59467             for (var c =0;c < 7;c++) {
59468                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
59469             }    
59470         }
59471         
59472         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59473         for(i=0;i<cells.length;i++) {
59474             
59475             this.cells.elements[i].dayName = cells[i].dayName ;
59476             this.cells.elements[i].className = cells[i].className;
59477             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
59478             this.cells.elements[i].title = cells[i].title ;
59479             this.cells.elements[i].dateValue = cells[i].dateValue ;
59480         }
59481         
59482         
59483         
59484         
59485         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
59486         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
59487         
59488         ////if(totalRows != 6){
59489             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
59490            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
59491        // }
59492         
59493         this.fireEvent('monthchange', this, date);
59494         
59495         
59496     },
59497  /**
59498      * Returns the grid's SelectionModel.
59499      * @return {SelectionModel}
59500      */
59501     getSelectionModel : function(){
59502         if(!this.selModel){
59503             this.selModel = new Roo.grid.CellSelectionModel();
59504         }
59505         return this.selModel;
59506     },
59507
59508     load: function() {
59509         this.eventStore.load()
59510         
59511         
59512         
59513     },
59514     
59515     findCell : function(dt) {
59516         dt = dt.clearTime().getTime();
59517         var ret = false;
59518         this.cells.each(function(c){
59519             //Roo.log("check " +c.dateValue + '?=' + dt);
59520             if(c.dateValue == dt){
59521                 ret = c;
59522                 return false;
59523             }
59524             return true;
59525         });
59526         
59527         return ret;
59528     },
59529     
59530     findCells : function(rec) {
59531         var s = rec.data.start_dt.clone().clearTime().getTime();
59532        // Roo.log(s);
59533         var e= rec.data.end_dt.clone().clearTime().getTime();
59534        // Roo.log(e);
59535         var ret = [];
59536         this.cells.each(function(c){
59537              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
59538             
59539             if(c.dateValue > e){
59540                 return ;
59541             }
59542             if(c.dateValue < s){
59543                 return ;
59544             }
59545             ret.push(c);
59546         });
59547         
59548         return ret;    
59549     },
59550     
59551     findBestRow: function(cells)
59552     {
59553         var ret = 0;
59554         
59555         for (var i =0 ; i < cells.length;i++) {
59556             ret  = Math.max(cells[i].rows || 0,ret);
59557         }
59558         return ret;
59559         
59560     },
59561     
59562     
59563     addItem : function(rec)
59564     {
59565         // look for vertical location slot in
59566         var cells = this.findCells(rec);
59567         
59568         rec.row = this.findBestRow(cells);
59569         
59570         // work out the location.
59571         
59572         var crow = false;
59573         var rows = [];
59574         for(var i =0; i < cells.length; i++) {
59575             if (!crow) {
59576                 crow = {
59577                     start : cells[i],
59578                     end :  cells[i]
59579                 };
59580                 continue;
59581             }
59582             if (crow.start.getY() == cells[i].getY()) {
59583                 // on same row.
59584                 crow.end = cells[i];
59585                 continue;
59586             }
59587             // different row.
59588             rows.push(crow);
59589             crow = {
59590                 start: cells[i],
59591                 end : cells[i]
59592             };
59593             
59594         }
59595         
59596         rows.push(crow);
59597         rec.els = [];
59598         rec.rows = rows;
59599         rec.cells = cells;
59600         for (var i = 0; i < cells.length;i++) {
59601             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
59602             
59603         }
59604         
59605         
59606     },
59607     
59608     clearEvents: function() {
59609         
59610         if (!this.eventStore.getCount()) {
59611             return;
59612         }
59613         // reset number of rows in cells.
59614         Roo.each(this.cells.elements, function(c){
59615             c.rows = 0;
59616         });
59617         
59618         this.eventStore.each(function(e) {
59619             this.clearEvent(e);
59620         },this);
59621         
59622     },
59623     
59624     clearEvent : function(ev)
59625     {
59626         if (ev.els) {
59627             Roo.each(ev.els, function(el) {
59628                 el.un('mouseenter' ,this.onEventEnter, this);
59629                 el.un('mouseleave' ,this.onEventLeave, this);
59630                 el.remove();
59631             },this);
59632             ev.els = [];
59633         }
59634     },
59635     
59636     
59637     renderEvent : function(ev,ctr) {
59638         if (!ctr) {
59639              ctr = this.view.el.select('.fc-event-container',true).first();
59640         }
59641         
59642          
59643         this.clearEvent(ev);
59644             //code
59645        
59646         
59647         
59648         ev.els = [];
59649         var cells = ev.cells;
59650         var rows = ev.rows;
59651         this.fireEvent('eventrender', this, ev);
59652         
59653         for(var i =0; i < rows.length; i++) {
59654             
59655             cls = '';
59656             if (i == 0) {
59657                 cls += ' fc-event-start';
59658             }
59659             if ((i+1) == rows.length) {
59660                 cls += ' fc-event-end';
59661             }
59662             
59663             //Roo.log(ev.data);
59664             // how many rows should it span..
59665             var cg = this.eventTmpl.append(ctr,Roo.apply({
59666                 fccls : cls
59667                 
59668             }, ev.data) , true);
59669             
59670             
59671             cg.on('mouseenter' ,this.onEventEnter, this, ev);
59672             cg.on('mouseleave' ,this.onEventLeave, this, ev);
59673             cg.on('click', this.onEventClick, this, ev);
59674             
59675             ev.els.push(cg);
59676             
59677             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
59678             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
59679             //Roo.log(cg);
59680              
59681             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
59682             cg.setWidth(ebox.right - sbox.x -2);
59683         }
59684     },
59685     
59686     renderEvents: function()
59687     {   
59688         // first make sure there is enough space..
59689         
59690         if (!this.eventTmpl) {
59691             this.eventTmpl = new Roo.Template(
59692                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
59693                     '<div class="fc-event-inner">' +
59694                         '<span class="fc-event-time">{time}</span>' +
59695                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
59696                     '</div>' +
59697                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
59698                 '</div>'
59699             );
59700                 
59701         }
59702                
59703         
59704         
59705         this.cells.each(function(c) {
59706             //Roo.log(c.select('.fc-day-content div',true).first());
59707             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
59708         });
59709         
59710         var ctr = this.view.el.select('.fc-event-container',true).first();
59711         
59712         var cls;
59713         this.eventStore.each(function(ev){
59714             
59715             this.renderEvent(ev);
59716              
59717              
59718         }, this);
59719         this.view.layout();
59720         
59721     },
59722     
59723     onEventEnter: function (e, el,event,d) {
59724         this.fireEvent('evententer', this, el, event);
59725     },
59726     
59727     onEventLeave: function (e, el,event,d) {
59728         this.fireEvent('eventleave', this, el, event);
59729     },
59730     
59731     onEventClick: function (e, el,event,d) {
59732         this.fireEvent('eventclick', this, el, event);
59733     },
59734     
59735     onMonthChange: function () {
59736         this.store.load();
59737     },
59738     
59739     onLoad: function () {
59740         
59741         //Roo.log('calendar onload');
59742 //         
59743         if(this.eventStore.getCount() > 0){
59744             
59745            
59746             
59747             this.eventStore.each(function(d){
59748                 
59749                 
59750                 // FIXME..
59751                 var add =   d.data;
59752                 if (typeof(add.end_dt) == 'undefined')  {
59753                     Roo.log("Missing End time in calendar data: ");
59754                     Roo.log(d);
59755                     return;
59756                 }
59757                 if (typeof(add.start_dt) == 'undefined')  {
59758                     Roo.log("Missing Start time in calendar data: ");
59759                     Roo.log(d);
59760                     return;
59761                 }
59762                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
59763                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
59764                 add.id = add.id || d.id;
59765                 add.title = add.title || '??';
59766                 
59767                 this.addItem(d);
59768                 
59769              
59770             },this);
59771         }
59772         
59773         this.renderEvents();
59774     }
59775     
59776
59777 });
59778 /*
59779  grid : {
59780                 xtype: 'Grid',
59781                 xns: Roo.grid,
59782                 listeners : {
59783                     render : function ()
59784                     {
59785                         _this.grid = this;
59786                         
59787                         if (!this.view.el.hasClass('course-timesheet')) {
59788                             this.view.el.addClass('course-timesheet');
59789                         }
59790                         if (this.tsStyle) {
59791                             this.ds.load({});
59792                             return; 
59793                         }
59794                         Roo.log('width');
59795                         Roo.log(_this.grid.view.el.getWidth());
59796                         
59797                         
59798                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
59799                             '.course-timesheet .x-grid-row' : {
59800                                 height: '80px'
59801                             },
59802                             '.x-grid-row td' : {
59803                                 'vertical-align' : 0
59804                             },
59805                             '.course-edit-link' : {
59806                                 'color' : 'blue',
59807                                 'text-overflow' : 'ellipsis',
59808                                 'overflow' : 'hidden',
59809                                 'white-space' : 'nowrap',
59810                                 'cursor' : 'pointer'
59811                             },
59812                             '.sub-link' : {
59813                                 'color' : 'green'
59814                             },
59815                             '.de-act-sup-link' : {
59816                                 'color' : 'purple',
59817                                 'text-decoration' : 'line-through'
59818                             },
59819                             '.de-act-link' : {
59820                                 'color' : 'red',
59821                                 'text-decoration' : 'line-through'
59822                             },
59823                             '.course-timesheet .course-highlight' : {
59824                                 'border-top-style': 'dashed !important',
59825                                 'border-bottom-bottom': 'dashed !important'
59826                             },
59827                             '.course-timesheet .course-item' : {
59828                                 'font-family'   : 'tahoma, arial, helvetica',
59829                                 'font-size'     : '11px',
59830                                 'overflow'      : 'hidden',
59831                                 'padding-left'  : '10px',
59832                                 'padding-right' : '10px',
59833                                 'padding-top' : '10px' 
59834                             }
59835                             
59836                         }, Roo.id());
59837                                 this.ds.load({});
59838                     }
59839                 },
59840                 autoWidth : true,
59841                 monitorWindowResize : false,
59842                 cellrenderer : function(v,x,r)
59843                 {
59844                     return v;
59845                 },
59846                 sm : {
59847                     xtype: 'CellSelectionModel',
59848                     xns: Roo.grid
59849                 },
59850                 dataSource : {
59851                     xtype: 'Store',
59852                     xns: Roo.data,
59853                     listeners : {
59854                         beforeload : function (_self, options)
59855                         {
59856                             options.params = options.params || {};
59857                             options.params._month = _this.monthField.getValue();
59858                             options.params.limit = 9999;
59859                             options.params['sort'] = 'when_dt';    
59860                             options.params['dir'] = 'ASC';    
59861                             this.proxy.loadResponse = this.loadResponse;
59862                             Roo.log("load?");
59863                             //this.addColumns();
59864                         },
59865                         load : function (_self, records, options)
59866                         {
59867                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
59868                                 // if you click on the translation.. you can edit it...
59869                                 var el = Roo.get(this);
59870                                 var id = el.dom.getAttribute('data-id');
59871                                 var d = el.dom.getAttribute('data-date');
59872                                 var t = el.dom.getAttribute('data-time');
59873                                 //var id = this.child('span').dom.textContent;
59874                                 
59875                                 //Roo.log(this);
59876                                 Pman.Dialog.CourseCalendar.show({
59877                                     id : id,
59878                                     when_d : d,
59879                                     when_t : t,
59880                                     productitem_active : id ? 1 : 0
59881                                 }, function() {
59882                                     _this.grid.ds.load({});
59883                                 });
59884                            
59885                            });
59886                            
59887                            _this.panel.fireEvent('resize', [ '', '' ]);
59888                         }
59889                     },
59890                     loadResponse : function(o, success, response){
59891                             // this is overridden on before load..
59892                             
59893                             Roo.log("our code?");       
59894                             //Roo.log(success);
59895                             //Roo.log(response)
59896                             delete this.activeRequest;
59897                             if(!success){
59898                                 this.fireEvent("loadexception", this, o, response);
59899                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59900                                 return;
59901                             }
59902                             var result;
59903                             try {
59904                                 result = o.reader.read(response);
59905                             }catch(e){
59906                                 Roo.log("load exception?");
59907                                 this.fireEvent("loadexception", this, o, response, e);
59908                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59909                                 return;
59910                             }
59911                             Roo.log("ready...");        
59912                             // loop through result.records;
59913                             // and set this.tdate[date] = [] << array of records..
59914                             _this.tdata  = {};
59915                             Roo.each(result.records, function(r){
59916                                 //Roo.log(r.data);
59917                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
59918                                     _this.tdata[r.data.when_dt.format('j')] = [];
59919                                 }
59920                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
59921                             });
59922                             
59923                             //Roo.log(_this.tdata);
59924                             
59925                             result.records = [];
59926                             result.totalRecords = 6;
59927                     
59928                             // let's generate some duumy records for the rows.
59929                             //var st = _this.dateField.getValue();
59930                             
59931                             // work out monday..
59932                             //st = st.add(Date.DAY, -1 * st.format('w'));
59933                             
59934                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
59935                             
59936                             var firstOfMonth = date.getFirstDayOfMonth();
59937                             var days = date.getDaysInMonth();
59938                             var d = 1;
59939                             var firstAdded = false;
59940                             for (var i = 0; i < result.totalRecords ; i++) {
59941                                 //var d= st.add(Date.DAY, i);
59942                                 var row = {};
59943                                 var added = 0;
59944                                 for(var w = 0 ; w < 7 ; w++){
59945                                     if(!firstAdded && firstOfMonth != w){
59946                                         continue;
59947                                     }
59948                                     if(d > days){
59949                                         continue;
59950                                     }
59951                                     firstAdded = true;
59952                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
59953                                     row['weekday'+w] = String.format(
59954                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
59955                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
59956                                                     d,
59957                                                     date.format('Y-m-')+dd
59958                                                 );
59959                                     added++;
59960                                     if(typeof(_this.tdata[d]) != 'undefined'){
59961                                         Roo.each(_this.tdata[d], function(r){
59962                                             var is_sub = '';
59963                                             var deactive = '';
59964                                             var id = r.id;
59965                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
59966                                             if(r.parent_id*1>0){
59967                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
59968                                                 id = r.parent_id;
59969                                             }
59970                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
59971                                                 deactive = 'de-act-link';
59972                                             }
59973                                             
59974                                             row['weekday'+w] += String.format(
59975                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
59976                                                     id, //0
59977                                                     r.product_id_name, //1
59978                                                     r.when_dt.format('h:ia'), //2
59979                                                     is_sub, //3
59980                                                     deactive, //4
59981                                                     desc // 5
59982                                             );
59983                                         });
59984                                     }
59985                                     d++;
59986                                 }
59987                                 
59988                                 // only do this if something added..
59989                                 if(added > 0){ 
59990                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
59991                                 }
59992                                 
59993                                 
59994                                 // push it twice. (second one with an hour..
59995                                 
59996                             }
59997                             //Roo.log(result);
59998                             this.fireEvent("load", this, o, o.request.arg);
59999                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
60000                         },
60001                     sortInfo : {field: 'when_dt', direction : 'ASC' },
60002                     proxy : {
60003                         xtype: 'HttpProxy',
60004                         xns: Roo.data,
60005                         method : 'GET',
60006                         url : baseURL + '/Roo/Shop_course.php'
60007                     },
60008                     reader : {
60009                         xtype: 'JsonReader',
60010                         xns: Roo.data,
60011                         id : 'id',
60012                         fields : [
60013                             {
60014                                 'name': 'id',
60015                                 'type': 'int'
60016                             },
60017                             {
60018                                 'name': 'when_dt',
60019                                 'type': 'string'
60020                             },
60021                             {
60022                                 'name': 'end_dt',
60023                                 'type': 'string'
60024                             },
60025                             {
60026                                 'name': 'parent_id',
60027                                 'type': 'int'
60028                             },
60029                             {
60030                                 'name': 'product_id',
60031                                 'type': 'int'
60032                             },
60033                             {
60034                                 'name': 'productitem_id',
60035                                 'type': 'int'
60036                             },
60037                             {
60038                                 'name': 'guid',
60039                                 'type': 'int'
60040                             }
60041                         ]
60042                     }
60043                 },
60044                 toolbar : {
60045                     xtype: 'Toolbar',
60046                     xns: Roo,
60047                     items : [
60048                         {
60049                             xtype: 'Button',
60050                             xns: Roo.Toolbar,
60051                             listeners : {
60052                                 click : function (_self, e)
60053                                 {
60054                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60055                                     sd.setMonth(sd.getMonth()-1);
60056                                     _this.monthField.setValue(sd.format('Y-m-d'));
60057                                     _this.grid.ds.load({});
60058                                 }
60059                             },
60060                             text : "Back"
60061                         },
60062                         {
60063                             xtype: 'Separator',
60064                             xns: Roo.Toolbar
60065                         },
60066                         {
60067                             xtype: 'MonthField',
60068                             xns: Roo.form,
60069                             listeners : {
60070                                 render : function (_self)
60071                                 {
60072                                     _this.monthField = _self;
60073                                    // _this.monthField.set  today
60074                                 },
60075                                 select : function (combo, date)
60076                                 {
60077                                     _this.grid.ds.load({});
60078                                 }
60079                             },
60080                             value : (function() { return new Date(); })()
60081                         },
60082                         {
60083                             xtype: 'Separator',
60084                             xns: Roo.Toolbar
60085                         },
60086                         {
60087                             xtype: 'TextItem',
60088                             xns: Roo.Toolbar,
60089                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
60090                         },
60091                         {
60092                             xtype: 'Fill',
60093                             xns: Roo.Toolbar
60094                         },
60095                         {
60096                             xtype: 'Button',
60097                             xns: Roo.Toolbar,
60098                             listeners : {
60099                                 click : function (_self, e)
60100                                 {
60101                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60102                                     sd.setMonth(sd.getMonth()+1);
60103                                     _this.monthField.setValue(sd.format('Y-m-d'));
60104                                     _this.grid.ds.load({});
60105                                 }
60106                             },
60107                             text : "Next"
60108                         }
60109                     ]
60110                 },
60111                  
60112             }
60113         };
60114         
60115         *//*
60116  * Based on:
60117  * Ext JS Library 1.1.1
60118  * Copyright(c) 2006-2007, Ext JS, LLC.
60119  *
60120  * Originally Released Under LGPL - original licence link has changed is not relivant.
60121  *
60122  * Fork - LGPL
60123  * <script type="text/javascript">
60124  */
60125  
60126 /**
60127  * @class Roo.LoadMask
60128  * A simple utility class for generically masking elements while loading data.  If the element being masked has
60129  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
60130  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
60131  * element's UpdateManager load indicator and will be destroyed after the initial load.
60132  * @constructor
60133  * Create a new LoadMask
60134  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
60135  * @param {Object} config The config object
60136  */
60137 Roo.LoadMask = function(el, config){
60138     this.el = Roo.get(el);
60139     Roo.apply(this, config);
60140     if(this.store){
60141         this.store.on('beforeload', this.onBeforeLoad, this);
60142         this.store.on('load', this.onLoad, this);
60143         this.store.on('loadexception', this.onLoadException, this);
60144         this.removeMask = false;
60145     }else{
60146         var um = this.el.getUpdateManager();
60147         um.showLoadIndicator = false; // disable the default indicator
60148         um.on('beforeupdate', this.onBeforeLoad, this);
60149         um.on('update', this.onLoad, this);
60150         um.on('failure', this.onLoad, this);
60151         this.removeMask = true;
60152     }
60153 };
60154
60155 Roo.LoadMask.prototype = {
60156     /**
60157      * @cfg {Boolean} removeMask
60158      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
60159      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
60160      */
60161     /**
60162      * @cfg {String} msg
60163      * The text to display in a centered loading message box (defaults to 'Loading...')
60164      */
60165     msg : 'Loading...',
60166     /**
60167      * @cfg {String} msgCls
60168      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
60169      */
60170     msgCls : 'x-mask-loading',
60171
60172     /**
60173      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
60174      * @type Boolean
60175      */
60176     disabled: false,
60177
60178     /**
60179      * Disables the mask to prevent it from being displayed
60180      */
60181     disable : function(){
60182        this.disabled = true;
60183     },
60184
60185     /**
60186      * Enables the mask so that it can be displayed
60187      */
60188     enable : function(){
60189         this.disabled = false;
60190     },
60191     
60192     onLoadException : function()
60193     {
60194         Roo.log(arguments);
60195         
60196         if (typeof(arguments[3]) != 'undefined') {
60197             Roo.MessageBox.alert("Error loading",arguments[3]);
60198         } 
60199         /*
60200         try {
60201             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
60202                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
60203             }   
60204         } catch(e) {
60205             
60206         }
60207         */
60208     
60209         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60210     },
60211     // private
60212     onLoad : function()
60213     {
60214         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60215     },
60216
60217     // private
60218     onBeforeLoad : function(){
60219         if(!this.disabled){
60220             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
60221         }
60222     },
60223
60224     // private
60225     destroy : function(){
60226         if(this.store){
60227             this.store.un('beforeload', this.onBeforeLoad, this);
60228             this.store.un('load', this.onLoad, this);
60229             this.store.un('loadexception', this.onLoadException, this);
60230         }else{
60231             var um = this.el.getUpdateManager();
60232             um.un('beforeupdate', this.onBeforeLoad, this);
60233             um.un('update', this.onLoad, this);
60234             um.un('failure', this.onLoad, this);
60235         }
60236     }
60237 };/*
60238  * Based on:
60239  * Ext JS Library 1.1.1
60240  * Copyright(c) 2006-2007, Ext JS, LLC.
60241  *
60242  * Originally Released Under LGPL - original licence link has changed is not relivant.
60243  *
60244  * Fork - LGPL
60245  * <script type="text/javascript">
60246  */
60247
60248
60249 /**
60250  * @class Roo.XTemplate
60251  * @extends Roo.Template
60252  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
60253 <pre><code>
60254 var t = new Roo.XTemplate(
60255         '&lt;select name="{name}"&gt;',
60256                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
60257         '&lt;/select&gt;'
60258 );
60259  
60260 // then append, applying the master template values
60261  </code></pre>
60262  *
60263  * Supported features:
60264  *
60265  *  Tags:
60266
60267 <pre><code>
60268       {a_variable} - output encoded.
60269       {a_variable.format:("Y-m-d")} - call a method on the variable
60270       {a_variable:raw} - unencoded output
60271       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
60272       {a_variable:this.method_on_template(...)} - call a method on the template object.
60273  
60274 </code></pre>
60275  *  The tpl tag:
60276 <pre><code>
60277         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
60278         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
60279         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
60280         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
60281   
60282         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
60283         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
60284 </code></pre>
60285  *      
60286  */
60287 Roo.XTemplate = function()
60288 {
60289     Roo.XTemplate.superclass.constructor.apply(this, arguments);
60290     if (this.html) {
60291         this.compile();
60292     }
60293 };
60294
60295
60296 Roo.extend(Roo.XTemplate, Roo.Template, {
60297
60298     /**
60299      * The various sub templates
60300      */
60301     tpls : false,
60302     /**
60303      *
60304      * basic tag replacing syntax
60305      * WORD:WORD()
60306      *
60307      * // you can fake an object call by doing this
60308      *  x.t:(test,tesT) 
60309      * 
60310      */
60311     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
60312
60313     /**
60314      * compile the template
60315      *
60316      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
60317      *
60318      */
60319     compile: function()
60320     {
60321         var s = this.html;
60322      
60323         s = ['<tpl>', s, '</tpl>'].join('');
60324     
60325         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
60326             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
60327             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
60328             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
60329             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
60330             m,
60331             id     = 0,
60332             tpls   = [];
60333     
60334         while(true == !!(m = s.match(re))){
60335             var forMatch   = m[0].match(nameRe),
60336                 ifMatch   = m[0].match(ifRe),
60337                 execMatch   = m[0].match(execRe),
60338                 namedMatch   = m[0].match(namedRe),
60339                 
60340                 exp  = null, 
60341                 fn   = null,
60342                 exec = null,
60343                 name = forMatch && forMatch[1] ? forMatch[1] : '';
60344                 
60345             if (ifMatch) {
60346                 // if - puts fn into test..
60347                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
60348                 if(exp){
60349                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
60350                 }
60351             }
60352             
60353             if (execMatch) {
60354                 // exec - calls a function... returns empty if true is  returned.
60355                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
60356                 if(exp){
60357                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
60358                 }
60359             }
60360             
60361             
60362             if (name) {
60363                 // for = 
60364                 switch(name){
60365                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
60366                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
60367                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
60368                 }
60369             }
60370             var uid = namedMatch ? namedMatch[1] : id;
60371             
60372             
60373             tpls.push({
60374                 id:     namedMatch ? namedMatch[1] : id,
60375                 target: name,
60376                 exec:   exec,
60377                 test:   fn,
60378                 body:   m[1] || ''
60379             });
60380             if (namedMatch) {
60381                 s = s.replace(m[0], '');
60382             } else { 
60383                 s = s.replace(m[0], '{xtpl'+ id + '}');
60384             }
60385             ++id;
60386         }
60387         this.tpls = [];
60388         for(var i = tpls.length-1; i >= 0; --i){
60389             this.compileTpl(tpls[i]);
60390             this.tpls[tpls[i].id] = tpls[i];
60391         }
60392         this.master = tpls[tpls.length-1];
60393         return this;
60394     },
60395     /**
60396      * same as applyTemplate, except it's done to one of the subTemplates
60397      * when using named templates, you can do:
60398      *
60399      * var str = pl.applySubTemplate('your-name', values);
60400      *
60401      * 
60402      * @param {Number} id of the template
60403      * @param {Object} values to apply to template
60404      * @param {Object} parent (normaly the instance of this object)
60405      */
60406     applySubTemplate : function(id, values, parent)
60407     {
60408         
60409         
60410         var t = this.tpls[id];
60411         
60412         
60413         try { 
60414             if(t.test && !t.test.call(this, values, parent)){
60415                 return '';
60416             }
60417         } catch(e) {
60418             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
60419             Roo.log(e.toString());
60420             Roo.log(t.test);
60421             return ''
60422         }
60423         try { 
60424             
60425             if(t.exec && t.exec.call(this, values, parent)){
60426                 return '';
60427             }
60428         } catch(e) {
60429             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
60430             Roo.log(e.toString());
60431             Roo.log(t.exec);
60432             return ''
60433         }
60434         try {
60435             var vs = t.target ? t.target.call(this, values, parent) : values;
60436             parent = t.target ? values : parent;
60437             if(t.target && vs instanceof Array){
60438                 var buf = [];
60439                 for(var i = 0, len = vs.length; i < len; i++){
60440                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
60441                 }
60442                 return buf.join('');
60443             }
60444             return t.compiled.call(this, vs, parent);
60445         } catch (e) {
60446             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
60447             Roo.log(e.toString());
60448             Roo.log(t.compiled);
60449             return '';
60450         }
60451     },
60452
60453     compileTpl : function(tpl)
60454     {
60455         var fm = Roo.util.Format;
60456         var useF = this.disableFormats !== true;
60457         var sep = Roo.isGecko ? "+" : ",";
60458         var undef = function(str) {
60459             Roo.log("Property not found :"  + str);
60460             return '';
60461         };
60462         
60463         var fn = function(m, name, format, args)
60464         {
60465             //Roo.log(arguments);
60466             args = args ? args.replace(/\\'/g,"'") : args;
60467             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
60468             if (typeof(format) == 'undefined') {
60469                 format= 'htmlEncode';
60470             }
60471             if (format == 'raw' ) {
60472                 format = false;
60473             }
60474             
60475             if(name.substr(0, 4) == 'xtpl'){
60476                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
60477             }
60478             
60479             // build an array of options to determine if value is undefined..
60480             
60481             // basically get 'xxxx.yyyy' then do
60482             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
60483             //    (function () { Roo.log("Property not found"); return ''; })() :
60484             //    ......
60485             
60486             var udef_ar = [];
60487             var lookfor = '';
60488             Roo.each(name.split('.'), function(st) {
60489                 lookfor += (lookfor.length ? '.': '') + st;
60490                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
60491             });
60492             
60493             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
60494             
60495             
60496             if(format && useF){
60497                 
60498                 args = args ? ',' + args : "";
60499                  
60500                 if(format.substr(0, 5) != "this."){
60501                     format = "fm." + format + '(';
60502                 }else{
60503                     format = 'this.call("'+ format.substr(5) + '", ';
60504                     args = ", values";
60505                 }
60506                 
60507                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
60508             }
60509              
60510             if (args.length) {
60511                 // called with xxyx.yuu:(test,test)
60512                 // change to ()
60513                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
60514             }
60515             // raw.. - :raw modifier..
60516             return "'"+ sep + udef_st  + name + ")"+sep+"'";
60517             
60518         };
60519         var body;
60520         // branched to use + in gecko and [].join() in others
60521         if(Roo.isGecko){
60522             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
60523                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
60524                     "';};};";
60525         }else{
60526             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
60527             body.push(tpl.body.replace(/(\r\n|\n)/g,
60528                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
60529             body.push("'].join('');};};");
60530             body = body.join('');
60531         }
60532         
60533         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
60534        
60535         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
60536         eval(body);
60537         
60538         return this;
60539     },
60540
60541     applyTemplate : function(values){
60542         return this.master.compiled.call(this, values, {});
60543         //var s = this.subs;
60544     },
60545
60546     apply : function(){
60547         return this.applyTemplate.apply(this, arguments);
60548     }
60549
60550  });
60551
60552 Roo.XTemplate.from = function(el){
60553     el = Roo.getDom(el);
60554     return new Roo.XTemplate(el.value || el.innerHTML);
60555 };